diff --git a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java index 815b337a..4a857130 100644 --- a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java +++ b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java @@ -1,250 +1,250 @@ /* * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.kde.kdeconnect.Backends.BluetoothBackend; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.os.Build; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; import org.kde.kdeconnect.NetworkPacket; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.nio.charset.Charset; import java.security.PublicKey; import java.util.UUID; public class BluetoothLink extends BaseLink { private final BluetoothSocket socket; private final BluetoothLinkProvider linkProvider; private boolean continueAccepting = true; private final Thread receivingThread = new Thread(new Runnable() { @Override public void run() { StringBuilder sb = new StringBuilder(); try { Reader reader = new InputStreamReader(socket.getInputStream(), "UTF-8"); char[] buf = new char[512]; while (continueAccepting) { while (sb.indexOf("\n") == -1 && continueAccepting) { int charsRead; if ((charsRead = reader.read(buf)) > 0) { sb.append(buf, 0, charsRead); } } int endIndex = sb.indexOf("\n"); if (endIndex != -1) { String message = sb.substring(0, endIndex + 1); sb.delete(0, endIndex + 1); processMessage(message); } } } catch (IOException e) { Log.e("BluetoothLink/receiving", "Connection to " + socket.getRemoteDevice().getAddress() + " likely broken.", e); disconnect(); } } private void processMessage(String message) { NetworkPacket np; try { np = NetworkPacket.unserialize(message); } catch (JSONException e) { Log.e("BluetoothLink/receiving", "Unable to parse message.", e); return; } if (np.getType().equals(NetworkPacket.PACKET_TYPE_ENCRYPTED)) { try { np = RsaHelper.decrypt(np, privateKey); } catch (Exception e) { Log.e("BluetoothLink/receiving", "Exception decrypting the package", e); } } if (np.hasPayloadTransferInfo()) { BluetoothSocket transferSocket = null; try { UUID transferUuid = UUID.fromString(np.getPayloadTransferInfo().getString("uuid")); transferSocket = socket.getRemoteDevice().createRfcommSocketToServiceRecord(transferUuid); transferSocket.connect(); np.setPayload(new NetworkPacket.Payload(transferSocket.getInputStream(), np.getPayloadSize())); } catch (Exception e) { if (transferSocket != null) { try { transferSocket.close(); } catch (IOException ignored) { } } Log.e("BluetoothLink/receiving", "Unable to get payload", e); } } packageReceived(np); } }); public BluetoothLink(Context context, BluetoothSocket socket, String deviceId, BluetoothLinkProvider linkProvider) { super(context, deviceId, linkProvider); this.socket = socket; this.linkProvider = linkProvider; } public void startListening() { this.receivingThread.start(); } @Override public String getName() { return "BluetoothLink"; } @Override public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) { return new BluetoothPairingHandler(device, callback); } public void disconnect() { if (socket == null) { return; } continueAccepting = false; try { socket.close(); - } catch (IOException e) { + } catch (IOException ignored) { } linkProvider.disconnectedLink(this, getDeviceId(), socket); } private void sendMessage(NetworkPacket np) throws JSONException, IOException { byte[] message = np.serialize().getBytes(Charset.forName("UTF-8")); OutputStream socket = this.socket.getOutputStream(); Log.i("BluetoothLink", "Beginning to send message"); socket.write(message); Log.i("BluetoothLink", "Finished sending message"); } @Override public boolean sendPacket(NetworkPacket np, Device.SendPacketStatusCallback callback) { return sendPacketInternal(np, callback, null); } @Override public boolean sendPacketEncrypted(NetworkPacket np, Device.SendPacketStatusCallback callback, PublicKey key) { return sendPacketInternal(np, callback, key); } private boolean sendPacketInternal(NetworkPacket np, final Device.SendPacketStatusCallback callback, PublicKey key) { /*if (!isConnected()) { Log.e("BluetoothLink", "sendPacketEncrypted failed: not connected"); callback.sendFailure(new Exception("Not connected")); return; }*/ try { BluetoothServerSocket serverSocket = null; if (np.hasPayload()) { UUID transferUuid = UUID.randomUUID(); serverSocket = BluetoothAdapter.getDefaultAdapter() .listenUsingRfcommWithServiceRecord("KDE Connect Transfer", transferUuid); JSONObject payloadTransferInfo = new JSONObject(); payloadTransferInfo.put("uuid", transferUuid.toString()); np.setPayloadTransferInfo(payloadTransferInfo); } if (key != null) { try { np = RsaHelper.encrypt(np, key); } catch (Exception e) { callback.onFailure(e); return false; } } sendMessage(np); if (serverSocket != null) { try (BluetoothSocket transferSocket = serverSocket.accept()) { serverSocket.close(); int idealBufferLength = 4096; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && transferSocket.getMaxReceivePacketSize() > 0) { idealBufferLength = transferSocket.getMaxReceivePacketSize(); } byte[] buffer = new byte[idealBufferLength]; int bytesRead; long progress = 0; InputStream stream = np.getPayload().getInputStream(); while ((bytesRead = stream.read(buffer)) != -1) { progress += bytesRead; transferSocket.getOutputStream().write(buffer, 0, bytesRead); if (np.getPayloadSize() > 0) { callback.onProgressChanged((int) (100 * progress / np.getPayloadSize())); } } transferSocket.getOutputStream().flush(); stream.close(); } catch (Exception e) { callback.onFailure(e); return false; } } callback.onSuccess(); return true; } catch (Exception e) { callback.onFailure(e); return false; } } @Override public boolean linkShouldBeKeptAlive() { return receivingThread.isAlive(); } /* public boolean isConnected() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { return socket.isConnected(); } else { return true; } } */ } diff --git a/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java b/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java index edae946a..3b0dc707 100644 --- a/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java +++ b/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java @@ -1,297 +1,297 @@ /* * Copyright 2014 Albert Vaca Cintora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.kde.kdeconnect.Backends.LanBackend; import android.content.Context; import android.util.Log; import org.json.JSONObject; import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.kde.kdeconnect.Helpers.StringsHelper; import org.kde.kdeconnect.NetworkPacket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.channels.NotYetConnectedException; import java.security.PublicKey; import javax.net.ssl.SSLSocket; public class LanLink extends BaseLink { public interface LinkDisconnectedCallback { void linkDisconnected(LanLink brokenLink); } public enum ConnectionStarted { Locally, Remotely } private ConnectionStarted connectionSource; // If the other device sent me a broadcast, // I should not close the connection with it // because it's probably trying to find me and // potentially ask for pairing. private volatile Socket socket = null; private final LinkDisconnectedCallback callback; @Override public void disconnect() { Log.i("LanLink/Disconnect","socket:"+ socket.hashCode()); try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } //Returns the old socket public Socket reset(final Socket newSocket, ConnectionStarted connectionSource) throws IOException { Socket oldSocket = socket; socket = newSocket; this.connectionSource = connectionSource; if (oldSocket != null) { oldSocket.close(); //This should cancel the readThread } //Log.e("LanLink", "Start listening"); //Create a thread to take care of incoming data for the new socket new Thread(() -> { try { BufferedReader reader = new BufferedReader(new InputStreamReader(newSocket.getInputStream(), StringsHelper.UTF8)); while (true) { String packet; try { packet = reader.readLine(); } catch (SocketTimeoutException e) { continue; } if (packet == null) { throw new IOException("End of stream"); } if (packet.isEmpty()) { continue; } NetworkPacket np = NetworkPacket.unserialize(packet); receivedNetworkPacket(np); } } catch (Exception e) { Log.i("LanLink", "Socket closed: " + newSocket.hashCode() + ". Reason: " + e.getMessage()); try { Thread.sleep(300); } catch (InterruptedException ignored) {} // Wait a bit because we might receive a new socket meanwhile boolean thereIsaANewSocket = (newSocket != socket); if (!thereIsaANewSocket) { callback.linkDisconnected(LanLink.this); } } }).start(); return oldSocket; } public LanLink(Context context, String deviceId, LanLinkProvider linkProvider, Socket socket, ConnectionStarted connectionSource) throws IOException { super(context, deviceId, linkProvider); callback = linkProvider; reset(socket, connectionSource); } @Override public String getName() { return "LanLink"; } @Override public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) { return new LanPairingHandler(device, callback); } //Blocking, do not call from main thread private boolean sendPacketInternal(NetworkPacket np, final Device.SendPacketStatusCallback callback, PublicKey key) { if (socket == null) { Log.e("KDE/sendPacket", "Not yet connected"); callback.onFailure(new NotYetConnectedException()); return false; } try { //Prepare socket for the payload final ServerSocket server; if (np.hasPayload()) { server = LanLinkProvider.openServerSocketOnFreePort(LanLinkProvider.PAYLOAD_TRANSFER_MIN_PORT); JSONObject payloadTransferInfo = new JSONObject(); payloadTransferInfo.put("port", server.getLocalPort()); np.setPayloadTransferInfo(payloadTransferInfo); } else { server = null; } //Encrypt if key provided if (key != null) { np = RsaHelper.encrypt(np, key); } //Log.e("LanLink/sendPacket", np.getType()); //Send body of the network package try { OutputStream writer = socket.getOutputStream(); writer.write(np.serialize().getBytes(StringsHelper.UTF8)); writer.flush(); } catch (Exception e) { disconnect(); //main socket is broken, disconnect throw e; } //Send payload if (server != null) { Socket payloadSocket = null; OutputStream outputStream = null; InputStream inputStream; try { //Wait a maximum of 10 seconds for the other end to establish a connection with our socket, close it afterwards server.setSoTimeout(10*1000); payloadSocket = server.accept(); //Convert to SSL if needed if (socket instanceof SSLSocket) { payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, false); } outputStream = payloadSocket.getOutputStream(); inputStream = np.getPayload().getInputStream(); Log.i("KDE/LanLink", "Beginning to send payload"); byte[] buffer = new byte[4096]; int bytesRead; long size = np.getPayloadSize(); long progress = 0; long timeSinceLastUpdate = -1; while ((bytesRead = inputStream.read(buffer)) != -1) { //Log.e("ok",""+bytesRead); progress += bytesRead; outputStream.write(buffer, 0, bytesRead); if (size > 0) { if (timeSinceLastUpdate + 500 < System.currentTimeMillis()) { //Report progress every half a second long percent = ((100 * progress) / size); callback.onProgressChanged((int) percent); timeSinceLastUpdate = System.currentTimeMillis(); } } } outputStream.flush(); Log.i("KDE/LanLink", "Finished sending payload ("+progress+" bytes written)"); } finally { - try { server.close(); } catch (Exception e) { } - try { payloadSocket.close(); } catch (Exception e) { } + try { server.close(); } catch (Exception ignored) { } + try { payloadSocket.close(); } catch (Exception ignored) { } np.getPayload().close(); - try { outputStream.close(); } catch (Exception e) { } + try { outputStream.close(); } catch (Exception ignored) { } } } callback.onSuccess(); return true; } catch (Exception e) { if (callback != null) { callback.onFailure(e); } return false; } finally { //Make sure we close the payload stream, if any if (np.hasPayload()) { np.getPayload().close(); } } } //Blocking, do not call from main thread @Override public boolean sendPacket(NetworkPacket np, Device.SendPacketStatusCallback callback) { return sendPacketInternal(np, callback, null); } //Blocking, do not call from main thread @Override public boolean sendPacketEncrypted(NetworkPacket np, Device.SendPacketStatusCallback callback, PublicKey key) { return sendPacketInternal(np, callback, key); } private void receivedNetworkPacket(NetworkPacket np) { if (np.getType().equals(NetworkPacket.PACKET_TYPE_ENCRYPTED)) { try { np = RsaHelper.decrypt(np, privateKey); } catch(Exception e) { e.printStackTrace(); Log.e("KDE/onPacketReceived","Exception decrypting the package"); } } if (np.hasPayloadTransferInfo()) { Socket payloadSocket = new Socket(); try { int tcpPort = np.getPayloadTransferInfo().getInt("port"); InetSocketAddress deviceAddress = (InetSocketAddress) socket.getRemoteSocketAddress(); payloadSocket.connect(new InetSocketAddress(deviceAddress.getAddress(), tcpPort)); // Use ssl if existing link is on ssl if (socket instanceof SSLSocket) { payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, true); } np.setPayload(new NetworkPacket.Payload(payloadSocket, np.getPayloadSize())); } catch (Exception e) { try { payloadSocket.close(); } catch(Exception ignored) { } e.printStackTrace(); Log.e("KDE/LanLink", "Exception connecting to payload remote socket"); } } packageReceived(np); } @Override public boolean linkShouldBeKeptAlive() { return true; //FIXME: Current implementation is broken, so for now we will keep links always established //We keep the remotely initiated connections, since the remotes require them if they want to request //pairing to us, or connections that are already paired. //return (connectionSource == ConnectionStarted.Remotely); } } diff --git a/src/org/kde/kdeconnect/NetworkPacket.java b/src/org/kde/kdeconnect/NetworkPacket.java index 7cbfa0c3..369e2a47 100644 --- a/src/org/kde/kdeconnect/NetworkPacket.java +++ b/src/org/kde/kdeconnect/NetworkPacket.java @@ -1,365 +1,365 @@ /* * Copyright 2014 Albert Vaca Cintora * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.kde.kdeconnect; import android.content.Context; import android.util.Log; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.kde.kdeconnect.Helpers.DeviceHelper; import org.kde.kdeconnect.Plugins.PluginFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class NetworkPacket { public final static int ProtocolVersion = 7; public final static String PACKET_TYPE_IDENTITY = "kdeconnect.identity"; public final static String PACKET_TYPE_PAIR = "kdeconnect.pair"; public final static String PACKET_TYPE_ENCRYPTED = "kdeconnect.encrypted"; public static Set protocolPacketTypes = new HashSet() {{ add(PACKET_TYPE_IDENTITY); add(PACKET_TYPE_PAIR); add(PACKET_TYPE_ENCRYPTED); }}; private long mId; String mType; private JSONObject mBody; private Payload mPayload; private JSONObject mPayloadTransferInfo; private NetworkPacket() { } public NetworkPacket(String type) { mId = System.currentTimeMillis(); mType = type; mBody = new JSONObject(); mPayload = null; mPayloadTransferInfo = new JSONObject(); } public String getType() { return mType; } public long getId() { return mId; } //Most commons getters and setters defined for convenience public String getString(String key) { return mBody.optString(key, ""); } public String getString(String key, String defaultValue) { return mBody.optString(key, defaultValue); } public void set(String key, String value) { if (value == null) return; try { mBody.put(key, value); - } catch (Exception e) { + } catch (Exception ignored) { } } public int getInt(String key) { return mBody.optInt(key, -1); } public int getInt(String key, int defaultValue) { return mBody.optInt(key, defaultValue); } public long getLong(String key) { return mBody.optLong(key, -1); } public long getLong(String key, long defaultValue) { return mBody.optLong(key, defaultValue); } public void set(String key, int value) { try { mBody.put(key, value); - } catch (Exception e) { + } catch (Exception ignored) { } } public boolean getBoolean(String key) { return mBody.optBoolean(key, false); } public boolean getBoolean(String key, boolean defaultValue) { return mBody.optBoolean(key, defaultValue); } public void set(String key, boolean value) { try { mBody.put(key, value); - } catch (Exception e) { + } catch (Exception ignored) { } } public double getDouble(String key) { return mBody.optDouble(key, Double.NaN); } public double getDouble(String key, double defaultValue) { return mBody.optDouble(key, defaultValue); } public void set(String key, double value) { try { mBody.put(key, value); - } catch (Exception e) { + } catch (Exception ignored) { } } public JSONArray getJSONArray(String key) { return mBody.optJSONArray(key); } public void set(String key, JSONArray value) { try { mBody.put(key, value); - } catch (Exception e) { + } catch (Exception ignored) { } } public JSONObject getJSONObject(String key) { return mBody.optJSONObject(key); } public void set(String key, JSONObject value) { try { mBody.put(key, value); - } catch (JSONException e) { + } catch (JSONException ignored) { } } private Set getStringSet(String key) { JSONArray jsonArray = mBody.optJSONArray(key); if (jsonArray == null) return null; Set list = new HashSet<>(); int length = jsonArray.length(); for (int i = 0; i < length; i++) { try { String str = jsonArray.getString(i); list.add(str); - } catch (Exception e) { + } catch (Exception ignored) { } } return list; } public Set getStringSet(String key, Set defaultValue) { if (mBody.has(key)) return getStringSet(key); else return defaultValue; } public void set(String key, Set value) { try { JSONArray jsonArray = new JSONArray(); for (String str : value) { jsonArray.put(str); } mBody.put(key, jsonArray); - } catch (Exception e) { + } catch (Exception ignored) { } } public List getStringList(String key) { JSONArray jsonArray = mBody.optJSONArray(key); if (jsonArray == null) return null; List list = new ArrayList<>(); int length = jsonArray.length(); for (int i = 0; i < length; i++) { try { String str = jsonArray.getString(i); list.add(str); - } catch (Exception e) { + } catch (Exception ignored) { } } return list; } public List getStringList(String key, List defaultValue) { if (mBody.has(key)) return getStringList(key); else return defaultValue; } public void set(String key, List value) { try { JSONArray jsonArray = new JSONArray(); for (String str : value) { jsonArray.put(str); } mBody.put(key, jsonArray); - } catch (Exception e) { + } catch (Exception ignored) { } } public boolean has(String key) { return mBody.has(key); } public String serialize() throws JSONException { JSONObject jo = new JSONObject(); jo.put("id", mId); jo.put("type", mType); jo.put("body", mBody); if (hasPayload()) { jo.put("payloadSize", mPayload.payloadSize); jo.put("payloadTransferInfo", mPayloadTransferInfo); } //QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format. return jo.toString().replace("\\/", "/") + "\n"; } static public NetworkPacket unserialize(String s) throws JSONException { NetworkPacket np = new NetworkPacket(); JSONObject jo = new JSONObject(s); np.mId = jo.getLong("id"); np.mType = jo.getString("type"); np.mBody = jo.getJSONObject("body"); if (jo.has("payloadSize")) { np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo"); np.mPayload = new Payload(jo.getLong("payloadSize")); } else { np.mPayloadTransferInfo = new JSONObject(); np.mPayload = new Payload(0); } return np; } static public NetworkPacket createIdentityPacket(Context context) { NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY); String deviceId = DeviceHelper.getDeviceId(context); try { np.mBody.put("deviceId", deviceId); np.mBody.put("deviceName", DeviceHelper.getDeviceName(context)); np.mBody.put("protocolVersion", NetworkPacket.ProtocolVersion); np.mBody.put("deviceType", DeviceHelper.getDeviceType(context).toString()); np.mBody.put("incomingCapabilities", new JSONArray(PluginFactory.getIncomingCapabilities())); np.mBody.put("outgoingCapabilities", new JSONArray(PluginFactory.getOutgoingCapabilities())); } catch (Exception e) { e.printStackTrace(); Log.e("NetworkPacakge", "Exception on createIdentityPacket"); } return np; } public void setPayload(Payload payload) { mPayload = payload; } public Payload getPayload() { return mPayload; } public long getPayloadSize() { return mPayload == null ? 0 : mPayload.payloadSize; } public boolean hasPayload() { return (mPayload != null && mPayload.payloadSize != 0); } public boolean hasPayloadTransferInfo() { return (mPayloadTransferInfo.length() > 0); } public JSONObject getPayloadTransferInfo() { return mPayloadTransferInfo; } public void setPayloadTransferInfo(JSONObject payloadTransferInfo) { mPayloadTransferInfo = payloadTransferInfo; } public static class Payload { private InputStream inputStream; private Socket inputSocket; private long payloadSize; Payload(long payloadSize) { this((InputStream)null, payloadSize); } public Payload(byte[] data) { this(new ByteArrayInputStream(data), data.length); } /** * NOTE: Do not use this to set an SSLSockets InputStream as the payload, use Payload(Socket, long) instead because of this bug */ public Payload(InputStream inputStream, long payloadSize) { this.inputSocket = null; this.inputStream = inputStream; this.payloadSize = payloadSize; } public Payload(Socket inputSocket, long payloadSize) throws IOException { this.inputSocket = inputSocket; this.inputStream = inputSocket.getInputStream(); this.payloadSize = payloadSize; } /** * NOTE: Do not close the InputStream directly call Payload.close() instead, this is because of this bug */ public InputStream getInputStream() { return inputStream; } long getPayloadSize() { return payloadSize; } public void close() { //TODO: If socket only close socket if that also closes the streams that is try { if (inputStream != null) { inputStream.close(); } } catch(IOException ignored) {} try { if (inputSocket != null) { inputSocket.close(); } } catch (IOException ignored) {} } } }