| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.android.tools.sdkcontroller.lib; |
| |
| import android.net.LocalSocket; |
| import android.util.Log; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.ByteOrder; |
| import java.nio.channels.ClosedChannelException; |
| |
| /** |
| * Encapsulates a connection with the emulator over a UNIX-domain socket. |
| */ |
| public class Socket { |
| /** UNIX-domain socket connected with the emulator. */ |
| private LocalSocket mSocket = null; |
| /** Channel name for the connection established via this socket. */ |
| private String mChannelName; |
| /** Endianness of data transferred in this connection. */ |
| private ByteOrder mEndian; |
| |
| /** Tag for message logging. */ |
| private static final String TAG = "SdkControllerSocket"; |
| /** Controls debug log. */ |
| private static boolean DEBUG = false; |
| |
| /** |
| * Constructs Socket instance. |
| * |
| * @param socket Socket connection with the emulator. |
| * @param name Channel port name for this connection. |
| * @param endian Endianness of data transferred in this connection. |
| */ |
| public Socket(LocalSocket socket, String name, ByteOrder endian) { |
| mSocket = socket; |
| mChannelName = name; |
| mEndian = endian; |
| if (DEBUG) Log.d(TAG, "Socket is constructed for " + mChannelName); |
| } |
| |
| /** |
| * Gets connection status of this socket. |
| * |
| * @return true if socket is connected, or false if socket is not connected. |
| */ |
| public boolean isConnected() { |
| return mSocket != null; |
| } |
| |
| /** |
| * Gets channel name for this socket. |
| * |
| * @return Channel name for this socket. |
| */ |
| public String getChannelName() { |
| return mChannelName; |
| } |
| |
| /** |
| * Gets endianness of data transferred via this socket. |
| * |
| * @return Endianness of data transferred via this socket. |
| */ |
| public ByteOrder getEndian() { |
| return mEndian; |
| } |
| |
| /** |
| * Sends data to the socket. |
| * |
| * @param data Data to send. Data size is defined by the length of the |
| * array. |
| * @throws IOException |
| */ |
| public void send(byte[] data) throws IOException { |
| // Use local copy of the socket, ensuring it's not going to NULL while |
| // we're working with it. If it gets closed, while we're in the middle |
| // of data transfer - it's OK, since it will produce an exception, and |
| // the caller will gracefully handle it. |
| // |
| // Same technique is used everywhere in this class where mSocket member |
| // is touched. |
| LocalSocket socket = mSocket; |
| if (socket == null) { |
| Logw("'send' request on closed Socket " + mChannelName); |
| throw new ClosedChannelException(); |
| } |
| socket.getOutputStream().write(data); |
| } |
| |
| /** |
| * Sends data to the socket. |
| * |
| * @param data Data to send. |
| * @param offset The start position in data from where to get bytes. |
| * @param len The number of bytes from data to write to this socket. |
| * @throws IOException |
| */ |
| public void send(byte[] data, int offset, int len) throws IOException { |
| LocalSocket socket = mSocket; |
| if (socket == null) { |
| Logw("'send' request on closed Socket " + mChannelName); |
| throw new ClosedChannelException(); |
| } |
| socket.getOutputStream().write(data, offset, len); |
| } |
| |
| /** |
| * Receives data from the socket. |
| * |
| * @param socket Socket from where to receive data. |
| * @param data Array where to save received data. |
| * @param len Number of bytes to receive. |
| * @throws IOException |
| */ |
| public static void receive(LocalSocket socket, byte[] data, int len) throws IOException { |
| final InputStream is = socket.getInputStream(); |
| int received = 0; |
| while (received != len) { |
| final int chunk = is.read(data, received, len - received); |
| if (chunk < 0) { |
| throw new IOException( |
| "I/O failure while receiving SDK controller data from socket."); |
| } |
| received += chunk; |
| } |
| } |
| |
| /** |
| * Receives data from the socket. |
| * |
| * @param data Array where to save received data. |
| * @param len Number of bytes to receive. |
| * @throws IOException |
| */ |
| public void receive(byte[] data, int len) throws IOException { |
| LocalSocket socket = mSocket; |
| if (socket == null) { |
| Logw("'receive' request on closed Socket " + mChannelName); |
| throw new ClosedChannelException(); |
| } |
| receive(socket, data, len); |
| } |
| |
| /** |
| * Receives data from the socket. |
| * |
| * @param data Array where to save received data. Data size is defined by |
| * the size of the array. |
| * @throws IOException |
| */ |
| public void receive(byte[] data) throws IOException { |
| receive(data, data.length); |
| } |
| |
| /** |
| * Closes the socket. |
| * |
| * @return true if socket has been closed in this call, or false if it had |
| * been already closed when this method has been called. |
| */ |
| public boolean close() { |
| // This is the only place in this class where we will null the socket |
| // object. Since this method can be called concurrently from different |
| // threads, lets do this under the lock. |
| LocalSocket socket; |
| synchronized (this) { |
| socket = mSocket; |
| mSocket = null; |
| } |
| if (socket != null) { |
| try { |
| // Force all I/O to stop before closing the socket. |
| socket.shutdownInput(); |
| socket.shutdownOutput(); |
| socket.close(); |
| if (DEBUG) Log.d(TAG, "Socket is closed for " + mChannelName); |
| return true; |
| } catch (IOException e) { |
| Loge("Exception " + e + " while closing Socket for " + mChannelName); |
| } |
| } |
| return false; |
| } |
| |
| /*************************************************************************** |
| * Logging wrappers |
| **************************************************************************/ |
| |
| private void Loge(String log) { |
| Log.e(TAG, log); |
| } |
| |
| private void Logw(String log) { |
| Log.w(TAG, log); |
| } |
| } |