OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / org / apache / harmony / nio / internal / SocketChannelImpl.java
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 package org.apache.harmony.nio.internal;
19
20 // BEGIN android-note
21 // In this class the address length was changed from long to int.
22 // END android-note
23
24 import java.io.FileDescriptor;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.net.ConnectException;
29 import java.net.InetAddress;
30 import java.net.InetSocketAddress;
31 import java.net.Socket;
32 import java.net.SocketAddress;
33 import java.net.SocketException;
34 import java.net.SocketUtils;
35 import java.net.UnknownHostException;
36 import java.nio.ByteBuffer;
37 import java.nio.channels.AlreadyConnectedException;
38 import java.nio.channels.ClosedChannelException;
39 import java.nio.channels.ConnectionPendingException;
40 import java.nio.channels.IllegalBlockingModeException;
41 import java.nio.channels.NoConnectionPendingException;
42 import java.nio.channels.NotYetConnectedException;
43 import java.nio.channels.SocketChannel;
44 import java.nio.channels.UnresolvedAddressException;
45 import java.nio.channels.UnsupportedAddressTypeException;
46 import java.nio.channels.spi.SelectorProvider;
47 import libcore.io.IoUtils;
48 import org.apache.harmony.luni.net.PlainSocketImpl;
49 import org.apache.harmony.luni.platform.FileDescriptorHandler;
50 import org.apache.harmony.luni.platform.INetworkSystem;
51 import org.apache.harmony.luni.platform.Platform;
52 import org.apache.harmony.nio.AddressUtil;
53
54 /*
55  * The default implementation class of java.nio.channels.SocketChannel.
56  */
57 class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler {
58
59     private static final int EOF = -1;
60
61     // The singleton to do the native network operation.
62     static final INetworkSystem networkSystem = Platform.getNetworkSystem();
63
64     // Status un-init, not initialized.
65     static final int SOCKET_STATUS_UNINIT = EOF;
66
67     // Status before connect.
68     static final int SOCKET_STATUS_UNCONNECTED = 0;
69
70     // Status connection pending.
71     static final int SOCKET_STATUS_PENDING = 1;
72
73     // Status after connection success.
74     static final int SOCKET_STATUS_CONNECTED = 2;
75
76     // Status closed.
77     static final int SOCKET_STATUS_CLOSED = 3;
78
79     // The descriptor to interact with native code.
80     FileDescriptor fd;
81
82     // Our internal Socket.
83     private SocketAdapter socket = null;
84
85     // The address to be connected.
86     InetSocketAddress connectAddress = null;
87
88     // Local address of the this socket (package private for adapter).
89     InetAddress localAddress = null;
90
91     // Local port number.
92     int localPort;
93
94     // At first, uninitialized.
95     int status = SOCKET_STATUS_UNINIT;
96
97     // Whether the socket is bound.
98     volatile boolean isBound = false;
99
100     private static class ReadLock {}
101     private final Object readLock = new ReadLock();
102
103     private static class WriteLock {}
104     private final Object writeLock = new WriteLock();
105
106     /*
107      * Constructor for creating a connected socket channel.
108      */
109     public SocketChannelImpl(SelectorProvider selectorProvider) throws IOException {
110         this(selectorProvider, true);
111     }
112
113     /*
114      * Constructor for creating an optionally connected socket channel.
115      */
116     public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) throws IOException {
117         super(selectorProvider);
118         fd = new FileDescriptor();
119         status = SOCKET_STATUS_UNCONNECTED;
120         if (connect) {
121             networkSystem.socket(fd, true);
122         }
123     }
124
125     /*
126      * Getting the internal Socket If we have not the socket, we create a new
127      * one.
128      */
129     @Override
130     synchronized public Socket socket() {
131         if (socket == null) {
132             try {
133                 InetAddress addr = null;
134                 int port = 0;
135                 if (connectAddress != null) {
136                     addr = connectAddress.getAddress();
137                     port = connectAddress.getPort();
138                 }
139                 socket = new SocketAdapter(new PlainSocketImpl(fd, localPort, addr, port), this);
140             } catch (SocketException e) {
141                 return null;
142             }
143         }
144         return socket;
145     }
146
147     @Override
148     synchronized public boolean isConnected() {
149         return status == SOCKET_STATUS_CONNECTED;
150     }
151
152     /*
153      * Status setting used by other class.
154      */
155     synchronized void setConnected() {
156         status = SOCKET_STATUS_CONNECTED;
157     }
158
159     void setBound(boolean flag) {
160         isBound = flag;
161     }
162
163     @Override
164     synchronized public boolean isConnectionPending() {
165         return status == SOCKET_STATUS_PENDING;
166     }
167
168     @Override
169     public boolean connect(SocketAddress socketAddress) throws IOException {
170         // status must be open and unconnected
171         checkUnconnected();
172
173         // check the address
174         InetSocketAddress inetSocketAddress = validateAddress(socketAddress);
175         InetAddress normalAddr = inetSocketAddress.getAddress();
176
177         // When connecting, map ANY address to Localhost
178         if (normalAddr.isAnyLocalAddress()) {
179             normalAddr = InetAddress.getLocalHost();
180         }
181
182         int port = inetSocketAddress.getPort();
183         String hostName = normalAddr.getHostName();
184         // security check
185         SecurityManager sm = System.getSecurityManager();
186         if (sm != null) {
187             sm.checkConnect(hostName, port);
188         }
189
190         // connect result
191         int result = EOF;
192         boolean finished = false;
193
194         try {
195             if (isBlocking()) {
196                 begin();
197                 networkSystem.connect(fd, normalAddr, port, 0);
198                 finished = true; // Or we'd have thrown an exception.
199             } else {
200                 finished = networkSystem.connectNonBlocking(fd, normalAddr, port);
201                 // set back to nonblocking to work around with a bug in portlib
202                 if (!isBlocking()) {
203                     IoUtils.setBlocking(fd, false);
204                 }
205             }
206             isBound = finished;
207         } catch (IOException e) {
208             if (e instanceof ConnectException && !isBlocking()) {
209                 status = SOCKET_STATUS_PENDING;
210             } else {
211                 if (isOpen()) {
212                     close();
213                     finished = true;
214                 }
215                 throw e;
216             }
217         } finally {
218             if (isBlocking()) {
219                 end(finished);
220             }
221         }
222
223         initLocalAddressAndPort();
224         connectAddress = inetSocketAddress;
225         if (socket != null) {
226             socket.socketImpl().initRemoteAddressAndPort(connectAddress.getAddress(),
227                     connectAddress.getPort());
228         }
229
230         synchronized (this) {
231             if (isBlocking()) {
232                 status = (finished ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_UNCONNECTED);
233             } else {
234                 status = SOCKET_STATUS_PENDING;
235             }
236         }
237         return finished;
238     }
239
240     private void initLocalAddressAndPort() {
241         localAddress = networkSystem.getSocketLocalAddress(fd);
242         localPort = networkSystem.getSocketLocalPort(fd);
243         if (socket != null) {
244             socket.socketImpl().initLocalPort(localPort);
245         }
246     }
247
248     @Override
249     public boolean finishConnect() throws IOException {
250         // status check
251         synchronized (this) {
252             if (!isOpen()) {
253                 throw new ClosedChannelException();
254             }
255             if (status == SOCKET_STATUS_CONNECTED) {
256                 return true;
257             }
258             if (status != SOCKET_STATUS_PENDING) {
259                 throw new NoConnectionPendingException();
260             }
261         }
262
263         boolean finished = false;
264         try {
265             begin();
266             final int WAIT_FOREVER = -1;
267             final int POLL = 0;
268             finished = networkSystem.isConnected(fd, isBlocking() ? WAIT_FOREVER : POLL);
269             isBound = finished;
270             initLocalAddressAndPort();
271         } catch (ConnectException e) {
272             if (isOpen()) {
273                 close();
274                 finished = true;
275             }
276             throw e;
277         } finally {
278             end(finished);
279         }
280
281         synchronized (this) {
282             status = (finished ? SOCKET_STATUS_CONNECTED : status);
283             isBound = finished;
284             // TPE: Workaround for bug that turns socket back to blocking
285             if (!isBlocking()) implConfigureBlocking(false);
286         }
287         return finished;
288     }
289
290     void finishAccept() {
291         initLocalAddressAndPort();
292     }
293
294     @Override
295     public int read(ByteBuffer target) throws IOException {
296         FileChannelImpl.checkWritable(target);
297         checkOpenConnected();
298         if (!target.hasRemaining()) {
299             return 0;
300         }
301
302         int readCount;
303         if (target.isDirect() || target.hasArray()) {
304             readCount = readImpl(target);
305             if (readCount > 0) {
306                 target.position(target.position() + readCount);
307             }
308         } else {
309             ByteBuffer readBuffer = null;
310             byte[] readArray = null;
311             readArray = new byte[target.remaining()];
312             readBuffer = ByteBuffer.wrap(readArray);
313             readCount = readImpl(readBuffer);
314             if (readCount > 0) {
315                 target.put(readArray, 0, readCount);
316             }
317         }
318         return readCount;
319     }
320
321     @Override
322     public long read(ByteBuffer[] targets, int offset, int length) throws IOException {
323         if (!isIndexValid(targets, offset, length)) {
324             throw new IndexOutOfBoundsException();
325         }
326
327         checkOpenConnected();
328         int totalCount = FileChannelImpl.calculateTotalRemaining(targets, offset, length, true);
329         if (totalCount == 0) {
330             return 0;
331         }
332         byte[] readArray = new byte[totalCount];
333         ByteBuffer readBuffer = ByteBuffer.wrap(readArray);
334         int readCount;
335         // read data to readBuffer, and then transfer data from readBuffer to
336         // targets.
337         readCount = readImpl(readBuffer);
338         if (readCount > 0) {
339             int left = readCount;
340             int index = offset;
341             // transfer data from readArray to targets
342             while (left > 0) {
343                 int putLength = Math.min(targets[index].remaining(), left);
344                 targets[index].put(readArray, readCount - left, putLength);
345                 index++;
346                 left -= putLength;
347             }
348         }
349         return readCount;
350     }
351
352     private boolean isIndexValid(ByteBuffer[] targets, int offset, int length) {
353         return (length >= 0) && (offset >= 0)
354                 && ((long) length + (long) offset <= targets.length);
355     }
356
357     /**
358      * Read from channel, and store the result in the target.
359      *
360      * @param target
361      *            output parameter
362      */
363     private int readImpl(ByteBuffer target) throws IOException {
364         synchronized (readLock) {
365             int readCount = 0;
366             try {
367                 if (isBlocking()) {
368                     begin();
369                 }
370                 int offset = target.position();
371                 int length = target.remaining();
372                 if (target.isDirect()) {
373                     // BEGIN android-changed
374                     // changed address from long to int
375                     int address = AddressUtil.getDirectBufferAddress(target);
376                     readCount = networkSystem.readDirect(fd, address + offset, length);
377                     // END android-changed
378                 } else {
379                     // target is assured to have array.
380                     byte[] array = target.array();
381                     offset += target.arrayOffset();
382                     readCount = networkSystem.read(fd, array, offset, length);
383                 }
384                 return readCount;
385             } finally {
386                 if (isBlocking()) {
387                     end(readCount > 0);
388                 }
389             }
390         }
391     }
392
393     @Override
394     public int write(ByteBuffer source) throws IOException {
395         if (null == source) {
396             throw new NullPointerException();
397         }
398         checkOpenConnected();
399         if (!source.hasRemaining()) {
400             return 0;
401         }
402         return writeImpl(source);
403     }
404
405     @Override
406     public long write(ByteBuffer[] sources, int offset, int length) throws IOException {
407         if (!isIndexValid(sources, offset, length)) {
408             throw new IndexOutOfBoundsException();
409         }
410
411         checkOpenConnected();
412         int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false);
413         if (count == 0) {
414             return 0;
415         }
416         ByteBuffer writeBuf = ByteBuffer.allocate(count);
417         for (int val = offset; val < length + offset; val++) {
418             ByteBuffer source = sources[val];
419             int oldPosition = source.position();
420             writeBuf.put(source);
421             source.position(oldPosition);
422         }
423         writeBuf.flip();
424         int result = writeImpl(writeBuf);
425         int val = offset;
426         int written = result;
427         while (result > 0) {
428             ByteBuffer source = sources[val];
429             int gap = Math.min(result, source.remaining());
430             source.position(source.position() + gap);
431             val++;
432             result -= gap;
433         }
434         return written;
435     }
436
437     /*
438      * Write the source. return the count of bytes written.
439      */
440     private int writeImpl(ByteBuffer source) throws IOException {
441         synchronized (writeLock) {
442             if (!source.hasRemaining()) {
443                 return 0;
444             }
445             int writeCount = 0;
446             try {
447                 int pos = source.position();
448                 int length = source.remaining();
449                 if (isBlocking()) {
450                     begin();
451                 }
452                 if (source.isDirect()) {
453                     int address = AddressUtil.getDirectBufferAddress(source);
454                     writeCount = networkSystem.writeDirect(fd, address, pos, length);
455                 } else if (source.hasArray()) {
456                     pos += source.arrayOffset();
457                     writeCount = networkSystem.write(fd, source.array(), pos, length);
458                 } else {
459                     byte[] array = new byte[length];
460                     source.get(array);
461                     writeCount = networkSystem.write(fd, array, 0, length);
462                 }
463                 source.position(pos + writeCount);
464             } finally {
465                 if (isBlocking()) {
466                     end(writeCount >= 0);
467                 }
468             }
469             return writeCount;
470         }
471     }
472
473     /*
474      * Status check, open and "connected", when read and write.
475      */
476     synchronized private void checkOpenConnected() throws ClosedChannelException {
477         if (!isOpen()) {
478             throw new ClosedChannelException();
479         }
480         if (!isConnected()) {
481             throw new NotYetConnectedException();
482         }
483     }
484
485     /*
486      * Status check, open and "unconnected", before connection.
487      */
488     synchronized private void checkUnconnected() throws IOException {
489         if (!isOpen()) {
490             throw new ClosedChannelException();
491         }
492         if (status == SOCKET_STATUS_CONNECTED) {
493             throw new AlreadyConnectedException();
494         }
495         if (status == SOCKET_STATUS_PENDING) {
496             throw new ConnectionPendingException();
497         }
498     }
499
500     /*
501      * Shared by this class and DatagramChannelImpl, to do the address transfer
502      * and check.
503      */
504     static InetSocketAddress validateAddress(SocketAddress socketAddress) {
505         if (null == socketAddress) {
506             throw new IllegalArgumentException();
507         }
508         if (!(socketAddress instanceof InetSocketAddress)) {
509             throw new UnsupportedAddressTypeException();
510         }
511         InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
512         if (inetSocketAddress.isUnresolved()) {
513             throw new UnresolvedAddressException();
514         }
515         return inetSocketAddress;
516     }
517
518     /*
519      * Get local address.
520      */
521     public InetAddress getLocalAddress() throws UnknownHostException {
522         if (!isBound) {
523             byte[] any_bytes = { 0, 0, 0, 0 };
524             return InetAddress.getByAddress(any_bytes);
525         }
526         return localAddress;
527     }
528
529     /*
530      * Do really closing action here.
531      */
532     @Override
533     synchronized protected void implCloseSelectableChannel() throws IOException {
534         if (SOCKET_STATUS_CLOSED != status) {
535             status = SOCKET_STATUS_CLOSED;
536             if (null != socket && !socket.isClosed()) {
537                 socket.close();
538             } else {
539                 networkSystem.close(fd);
540             }
541         }
542     }
543
544     @Override
545     protected void implConfigureBlocking(boolean blockMode) throws IOException {
546         synchronized (blockingLock()) {
547             IoUtils.setBlocking(fd, blockMode);
548         }
549     }
550
551     /*
552      * Get the fd.
553      */
554     public FileDescriptor getFD() {
555         return fd;
556     }
557
558     /*
559      * Adapter classes for internal socket.
560      */
561     private static class SocketAdapter extends Socket {
562         private final SocketChannelImpl channel;
563         private final PlainSocketImpl socketImpl;
564
565         SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel) throws SocketException {
566             super(socketImpl);
567             this.socketImpl = socketImpl;
568             this.channel = channel;
569             SocketUtils.setCreated(this);
570         }
571
572         PlainSocketImpl socketImpl() {
573             return socketImpl;
574         }
575
576         @Override
577         public SocketChannel getChannel() {
578             return channel;
579         }
580
581         @Override
582         public boolean isBound() {
583             return channel.isBound;
584         }
585
586         @Override
587         public boolean isConnected() {
588             return channel.isConnected();
589         }
590
591         @Override
592         public InetAddress getLocalAddress() {
593             try {
594                 return channel.getLocalAddress();
595             } catch (UnknownHostException e) {
596                 return null;
597             }
598         }
599
600         @Override
601         public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
602             if (!channel.isBlocking()) {
603                 throw new IllegalBlockingModeException();
604             }
605             if (isConnected()) {
606                 throw new AlreadyConnectedException();
607             }
608             super.connect(remoteAddr, timeout);
609             channel.initLocalAddressAndPort();
610             if (super.isConnected()) {
611                 channel.setConnected();
612                 channel.isBound = super.isBound();
613             }
614         }
615
616         @Override
617         public void bind(SocketAddress localAddr) throws IOException {
618             if (channel.isConnected()) {
619                 throw new AlreadyConnectedException();
620             }
621             if (SocketChannelImpl.SOCKET_STATUS_PENDING == channel.status) {
622                 throw new ConnectionPendingException();
623             }
624             super.bind(localAddr);
625             // keep here to see if need next version
626             // channel.Address = getLocalSocketAddress();
627             // channel.localport = getLocalPort();
628             channel.isBound = true;
629         }
630
631         @Override
632         public void close() throws IOException {
633             synchronized (channel) {
634                 if (channel.isOpen()) {
635                     channel.close();
636                 } else {
637                     super.close();
638                 }
639                 channel.status = SocketChannelImpl.SOCKET_STATUS_CLOSED;
640             }
641         }
642
643         @Override
644         public OutputStream getOutputStream() throws IOException {
645             checkOpenAndConnected();
646             if (isOutputShutdown()) {
647                 throw new SocketException("Socket output is shutdown");
648             }
649             return new SocketChannelOutputStream(channel);
650         }
651
652         @Override
653         public InputStream getInputStream() throws IOException {
654             checkOpenAndConnected();
655             if (isInputShutdown()) {
656                 throw new SocketException("Socket input is shutdown");
657             }
658             return new SocketChannelInputStream(channel);
659         }
660
661         private void checkOpenAndConnected() throws SocketException {
662             if (!channel.isOpen()) {
663                 throw new SocketException("Socket is closed");
664             }
665             if (!channel.isConnected()) {
666                 throw new SocketException("Socket is not connected");
667             }
668         }
669     }
670
671     /*
672      * This output stream delegates all operations to the associated channel.
673      * Throws an IllegalBlockingModeException if the channel is in non-blocking
674      * mode when performing write operations.
675      */
676     private static class SocketChannelOutputStream extends OutputStream {
677         private final SocketChannel channel;
678
679         public SocketChannelOutputStream(SocketChannel channel) {
680             this.channel = channel;
681         }
682
683         /*
684          * Closes this stream and channel.
685          *
686          * @exception IOException thrown if an error occurs during the close
687          */
688         @Override
689         public void close() throws IOException {
690             channel.close();
691         }
692
693         @Override
694         public void write(byte[] buffer, int offset, int count) throws IOException {
695             if (0 > offset || 0 > count || count + offset > buffer.length) {
696                 throw new IndexOutOfBoundsException();
697             }
698             ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count);
699             if (!channel.isBlocking()) {
700                 throw new IllegalBlockingModeException();
701             }
702             channel.write(buf);
703         }
704
705         @Override
706         public void write(int oneByte) throws IOException {
707             if (!channel.isBlocking()) {
708                 throw new IllegalBlockingModeException();
709             }
710             ByteBuffer buffer = ByteBuffer.allocate(1);
711             buffer.put(0, (byte) (oneByte & 0xFF));
712             channel.write(buffer);
713         }
714     }
715
716     /*
717      * This input stream delegates all operations to the associated channel.
718      * Throws an IllegalBlockingModeException if the channel is in non-blocking
719      * mode when performing read operations.
720      */
721     private static class SocketChannelInputStream extends InputStream {
722         private final SocketChannel channel;
723
724         public SocketChannelInputStream(SocketChannel channel) {
725             this.channel = channel;
726         }
727
728         /*
729          * Closes this stream and channel.
730          */
731         @Override
732         public void close() throws IOException {
733             channel.close();
734         }
735
736         @Override
737         public int read() throws IOException {
738             if (!channel.isBlocking()) {
739                 throw new IllegalBlockingModeException();
740             }
741             ByteBuffer buf = ByteBuffer.allocate(1);
742             int result = channel.read(buf);
743             // BEGIN android-changed: input was already consumed
744             return (-1 == result) ? result : buf.get(0) & 0xFF;
745             // END android-changed
746         }
747
748         @Override
749         public int read(byte[] buffer, int offset, int count) throws IOException {
750             if (0 > offset || 0 > count || count + offset > buffer.length) {
751                 throw new IndexOutOfBoundsException();
752             }
753             if (!channel.isBlocking()) {
754                 throw new IllegalBlockingModeException();
755             }
756             ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count);
757             return channel.read(buf);
758         }
759     }
760 }