blob: 08e6b2813ed71042f259bc2b08105b6c77c92ff0 [file] [log] [blame]
/*
* 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);
}
}