package org.deviceconnect.android.deviceplugin.awsiot.udt;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
class SocketTask {
private static final boolean DEBUG = false;
private static final String TAG = "UDT";
private static final byte[] HEADER = {
0x01, 0x02, 0x03, 0x04
};
private static final int BUFFER_SIZE = 512;
private boolean mCloseFlag = false;
private Socket mSocket;
private final byte[] mRecvBuffer = new byte[4];
private final byte[] mSendBuffer = new byte[4];
private OnSocketTaskListener mListener;
public SocketTask(final Socket socket) {
if (socket == null) {
throw new NullPointerException("socket is null.");
}
mSocket = socket;
}
public void setOnSocketTaskListener(final OnSocketTaskListener listener) {
mListener = listener;
}
public void close() throws IOException {
mCloseFlag = true;
mSocket.close();
}
public void sendData(final byte[] data) throws IOException {
sendData(data, data.length);
}
public void sendData(final byte[] data, final int length) throws IOException {
sendData(data, 0, length);
}
public void sendData(final byte[] data, final int offset, final int length) throws IOException {
if (offset < 0) {
throw new IllegalArgumentException("offset is negative.");
}
if (length < 0) {
throw new IllegalArgumentException("length is negative.");
}
if (data.length < offset + length) {
throw new IllegalArgumentException("offset or length is invalid.");
}
intToByte(length, mSendBuffer, 0);
mSocket.getOutputStream().write(HEADER);
mSocket.getOutputStream().write(mSendBuffer);
int _offset = offset;
int _length;
while (_offset < offset + length) {
_length = offset + length - _offset;
if (_length > BUFFER_SIZE) {
_length = BUFFER_SIZE;
}
mSocket.getOutputStream().write(data, _offset, _length);
_offset += _length;
}
}
public boolean execute() {
final byte[] data = new byte[BUFFER_SIZE];
final String address = getRemoteAddress();
final int port = getRemotePort();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
if (mListener != null) {
mListener.onConnected(address, port);
}
int len;
final InputStream is = mSocket.getInputStream();
while (!mCloseFlag) {
// TODO データの読み込みがずれた時にリカバリー処理
readHeader(is);
int size = readSize(is);
while (size > 0) {
if (size < BUFFER_SIZE) {
len = is.read(data, 0, size);
} else {
len = is.read(data, 0, BUFFER_SIZE);
}
if (len < 0) {
continue;
}
out.write(data, 0, len);
size -= len;
if (mListener != null) {
mListener.onReceivedData(out.toByteArray(), address, port);
}
out.reset();
}
}
return true;
} catch (Exception e) {
if (DEBUG) {
Log.w(TAG, "SocketTask#execute", e);
}
return false;
} finally {
if (mListener != null) {
mListener.onDisconnected(address, port);
}
}
}
private boolean readHeader(final InputStream is) throws IOException {
is.read(mRecvBuffer, 0 ,4);
return mRecvBuffer[0] == HEADER[0] && mRecvBuffer[1] == HEADER[1] &&
mRecvBuffer[2] == HEADER[2] && mRecvBuffer[3] == HEADER[3];
}
private int readSize(final InputStream is) throws IOException {
is.read(mRecvBuffer, 0, 4);
return byteToInt(mRecvBuffer, 0);
}
private void intToByte(final int value, final byte[] buffer, final int offset) {
buffer[offset] = (byte) (value & 0xff);
buffer[offset + 1] = (byte) ((value >> 8) & 0xff);
buffer[offset + 2] = (byte) ((value >> 16) & 0xff);
buffer[offset + 3] = (byte) ((value >> 24) & 0xff);
}
private int byteToInt(final byte[] buffer, final int offset) {
return (buffer[offset] & 0xff) | ((buffer[offset + 1] & 0xff) << 8)
| ((buffer[offset + 2] & 0xff) << 16) | ((buffer[offset + 3] & 0xff) << 24);
}
private String getRemoteAddress() {
return ((InetSocketAddress) mSocket.getRemoteSocketAddress()).getAddress().getHostAddress();
}
private int getRemotePort() {
return ((InetSocketAddress) mSocket.getRemoteSocketAddress()).getPort();
}
public interface OnSocketTaskListener {
void onConnected(String address, int port);
void onReceivedData(byte[] data, String address, int port);
void onDisconnected(String address, int port);
}
}