1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.tools.sdkcontroller.lib;
18 
19 import android.net.LocalSocket;
20 import android.util.Log;
21 
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.nio.ByteOrder;
25 import java.nio.channels.ClosedChannelException;
26 
27 /**
28  * Encapsulates a connection with the emulator over a UNIX-domain socket.
29  */
30 public class Socket {
31     /** UNIX-domain socket connected with the emulator. */
32     private LocalSocket mSocket = null;
33     /** Channel name for the connection established via this socket. */
34     private String mChannelName;
35     /** Endianness of data transferred in this connection. */
36     private ByteOrder mEndian;
37 
38     /** Tag for message logging. */
39     private static final String TAG = "SdkControllerSocket";
40     /** Controls debug log. */
41     private static boolean DEBUG = false;
42 
43     /**
44      * Constructs Socket instance.
45      *
46      * @param socket Socket connection with the emulator.
47      * @param name Channel port name for this connection.
48      * @param endian Endianness of data transferred in this connection.
49      */
Socket(LocalSocket socket, String name, ByteOrder endian)50     public Socket(LocalSocket socket, String name, ByteOrder endian) {
51         mSocket = socket;
52         mChannelName = name;
53         mEndian = endian;
54         if (DEBUG) Log.d(TAG, "Socket is constructed for " + mChannelName);
55     }
56 
57     /**
58      * Gets connection status of this socket.
59      *
60      * @return true if socket is connected, or false if socket is not connected.
61      */
isConnected()62     public boolean isConnected() {
63         return mSocket != null;
64     }
65 
66     /**
67      * Gets channel name for this socket.
68      *
69      * @return Channel name for this socket.
70      */
getChannelName()71     public String getChannelName() {
72         return mChannelName;
73     }
74 
75     /**
76      * Gets endianness of data transferred via this socket.
77      *
78      * @return Endianness of data transferred via this socket.
79      */
getEndian()80     public ByteOrder getEndian() {
81         return mEndian;
82     }
83 
84     /**
85      * Sends data to the socket.
86      *
87      * @param data Data to send. Data size is defined by the length of the
88      *            array.
89      * @throws IOException
90      */
send(byte[] data)91     public void send(byte[] data) throws IOException {
92         // Use local copy of the socket, ensuring it's not going to NULL while
93         // we're working with it. If it gets closed, while we're in the middle
94         // of data transfer - it's OK, since it will produce an exception, and
95         // the caller will gracefully handle it.
96         //
97         // Same technique is used everywhere in this class where mSocket member
98         // is touched.
99         LocalSocket socket = mSocket;
100         if (socket == null) {
101             Logw("'send' request on closed Socket " + mChannelName);
102             throw new ClosedChannelException();
103         }
104         socket.getOutputStream().write(data);
105     }
106 
107     /**
108      * Sends data to the socket.
109      *
110      * @param data Data to send.
111      * @param offset The start position in data from where to get bytes.
112      * @param len The number of bytes from data to write to this socket.
113      * @throws IOException
114      */
send(byte[] data, int offset, int len)115     public void send(byte[] data, int offset, int len) throws IOException {
116         LocalSocket socket = mSocket;
117         if (socket == null) {
118             Logw("'send' request on closed Socket " + mChannelName);
119             throw new ClosedChannelException();
120         }
121         socket.getOutputStream().write(data, offset, len);
122     }
123 
124     /**
125      * Receives data from the socket.
126      *
127      * @param socket Socket from where to receive data.
128      * @param data Array where to save received data.
129      * @param len Number of bytes to receive.
130      * @throws IOException
131      */
receive(LocalSocket socket, byte[] data, int len)132     public static void receive(LocalSocket socket, byte[] data, int len) throws IOException {
133         final InputStream is = socket.getInputStream();
134         int received = 0;
135         while (received != len) {
136             final int chunk = is.read(data, received, len - received);
137             if (chunk < 0) {
138                 throw new IOException(
139                         "I/O failure while receiving SDK controller data from socket.");
140             }
141             received += chunk;
142         }
143     }
144 
145     /**
146      * Receives data from the socket.
147      *
148      * @param data Array where to save received data.
149      * @param len Number of bytes to receive.
150      * @throws IOException
151      */
receive(byte[] data, int len)152     public void receive(byte[] data, int len) throws IOException {
153         LocalSocket socket = mSocket;
154         if (socket == null) {
155             Logw("'receive' request on closed Socket " + mChannelName);
156             throw new ClosedChannelException();
157         }
158         receive(socket, data, len);
159     }
160 
161     /**
162      * Receives data from the socket.
163      *
164      * @param data Array where to save received data. Data size is defined by
165      *            the size of the array.
166      * @throws IOException
167      */
receive(byte[] data)168     public void receive(byte[] data) throws IOException {
169         receive(data, data.length);
170     }
171 
172     /**
173      * Closes the socket.
174      *
175      * @return true if socket has been closed in this call, or false if it had
176      *         been already closed when this method has been called.
177      */
close()178     public boolean close() {
179         // This is the only place in this class where we will null the socket
180         // object. Since this method can be called concurrently from different
181         // threads, lets do this under the lock.
182         LocalSocket socket;
183         synchronized (this) {
184             socket = mSocket;
185             mSocket = null;
186         }
187         if (socket != null) {
188             try {
189                 // Force all I/O to stop before closing the socket.
190                 socket.shutdownInput();
191                 socket.shutdownOutput();
192                 socket.close();
193                 if (DEBUG) Log.d(TAG, "Socket is closed for " + mChannelName);
194                 return true;
195             } catch (IOException e) {
196                 Loge("Exception " + e + " while closing Socket for " + mChannelName);
197             }
198         }
199         return false;
200     }
201 
202     /***************************************************************************
203      * Logging wrappers
204      **************************************************************************/
205 
Loge(String log)206     private void Loge(String log) {
207         Log.e(TAG, log);
208     }
209 
Logw(String log)210     private void Logw(String log) {
211         Log.w(TAG, log);
212     }
213 }
214