gt:     protected abstract void close() throws IOException;
context: package com.o3dr.android.client.utils.connection;
  
  import android.os.Handler;
  import android.os.Process;
  import android.os.RemoteException;
  import android.util.Log;
  
  import com.o3dr.services.android.lib.model.ICommandListener;
  
  import java.io.IOException;
  import java.io.InterruptedIOException;
  import java.nio.ByteBuffer;
  import java.util.concurrent.LinkedBlockingQueue;
  import java.util.concurrent.atomic.AtomicInteger;
  
  /**
   * Base class for ip connection (tcp, udp).
   */
  public abstract class AbstractIpConnection {
  
      private static final String TAG = AbstractIpConnection.class.getSimpleName();
  
      public static final int CONNECTION_TIMEOUT = 15 * 1000; //5 seconds
  
      /*
      Connection state
       */
      public static final int STATE_DISCONNECTED = 0;
      public static final int STATE_CONNECTING = 1;
      public static final int STATE_CONNECTED = 2;
  
      /**
       * Size of the buffer used to read messages from the connection.
       */
      private static final int DEFAULT_READ_BUFFER_SIZE = 1048576;
  
      private IpConnectionListener ipConnectionListener;
  
      /**
       * Queue the set of packets to send.
       * A thread will be blocking on it until there's element(s) available to send.
       */
      private final LinkedBlockingQueue<PacketData> packetsToSend = new LinkedBlockingQueue<>();
  
      private final AtomicInteger connectionStatus = new AtomicInteger(STATE_DISCONNECTED);
  
      private final boolean isSendingDisabled;
      private final boolean isReadingDisabled;
  
      private final ByteBuffer readBuffer;
  
      private final Runnable managerTask = new Runnable() {
          @Override
          public void run() {
              Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
  
              Thread sendingThread = null;
  
              try {
                  try {
                      open();
                      connectionStatus.set(STATE_CONNECTED);
                      if(ipConnectionListener != null)
                          ipConnectionListener.onIpConnected();
                  } catch (IOException e) {
                      Log.e(TAG, "Unable to open ip connection.", e);
                      return;
                  }
  
                  if(!isSendingDisabled) {
                      //Launch the packet dispatching thread
                      sendingThread = new Thread(sendingTask, "IP Connection-Sending Thread");
                      sendingThread.start();
                  }
  
                  if(!isReadingDisabled) {
                      try {
                          while (connectionStatus.get() == STATE_CONNECTED) {
                              readBuffer.clear();
                              try {
                                  int packetSize = read(readBuffer);
                                  if (packetSize > 0) {
                                      readBuffer.limit(packetSize);
  
                                      if (ipConnectionListener != null) {
                                          readBuffer.rewind();
                                          ipConnectionListener.onPacketReceived(readBuffer);
                                      }
                                  }
                              }catch(InterruptedIOException e){
                                  if(!isPolling)
                                      throw e;
                              }
                          }
                      } catch (IOException e) {
                          Log.e(TAG, "Error occurred while reading from the connection.", e);
                      }
                  }
                  else if(sendingThread != null){
                      try {
                          sendingThread.join();
                      } catch (InterruptedException e) {
                          Log.e(TAG, "Error while waiting for sending thread to complete.", e);
                      }
                  }
              }
              finally{
                  if(sendingThread != null && sendingThread.isAlive())
                      sendingThread.interrupt();
  
                  disconnect();
                  Log.i(TAG, "Exiting connection manager thread.");
              }
          }
      };
  
      /**
       * Blocks until there's packet(s) to send, then dispatch them.
       */
      private final Runnable sendingTask = new Runnable() {
          @Override
          public void run() {
              try{
                  while(connectionStatus.get() == STATE_CONNECTED){
                      final PacketData packetData = packetsToSend.take();
                      final ICommandListener listener = packetData.listener;
  
                      try {
                          send(packetData);
                          postSendSuccess(listener);
                      } catch (IOException e) {
                          Log.e(TAG, "Error occurred while sending packet.", e);
                          postSendTimeout(listener);
                      }
                  }
              } catch (InterruptedException e) {
                  Log.e(TAG, "Dispatching thread was interrupted.", e);
              }
              finally{
                  disconnect();
                  Log.i(TAG, "Exiting packet dispatcher thread.");
              }
          }
  
          private void postSendSuccess(final ICommandListener listener){
              if(handler == null || listener == null)
                  return;
  
              handler.post(new Runnable() {
                  @Override
                  public void run() {
                      try {
                          listener.onSuccess();
                      } catch (RemoteException e) {
                          Log.e(TAG, e.getMessage(), e);
                      }
                  }
              });
          }
  
          private void postSendTimeout(final ICommandListener listener){
              if(handler == null || listener == null)
                  return;
  
              handler.post(new Runnable() {
                  @Override
                  public void run() {
                      try {
                          listener.onTimeout();
                      } catch (RemoteException e) {
                          Log.e(TAG, e.getMessage(), e);
                      }
                  }
              });
          }
      };
  
      private final boolean isPolling;
      private final Handler handler;
  
      private Thread managerThread;
  
      public AbstractIpConnection(Handler handler){
          this(handler, false, false);
      }
  
      public AbstractIpConnection(Handler handler, int readBufferSize, boolean isPolling){
          this(handler, readBufferSize, false, false, isPolling);
      }
  
      public AbstractIpConnection(Handler handler, boolean disableSending, boolean disableReading){
          this(handler, DEFAULT_READ_BUFFER_SIZE, disableSending, disableReading, false);
      }
  
      public AbstractIpConnection(Handler handler, int readBufferSize, boolean disableSending, boolean disableReading, boolean isPolling){
          this.handler = handler;
          this.readBuffer = ByteBuffer.allocate(readBufferSize);
          isReadingDisabled = disableReading;
          isSendingDisabled = disableSending;
          this.isPolling = isPolling;
      }
  
      protected abstract void open() throws IOException;
  
      protected abstract int read(ByteBuffer buffer) throws IOException;
  
      protected abstract void send(PacketData data) throws IOException;
  
  