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