private SSLEngineResult engineUnwrap(SSLEngine engine, ByteBuffer input, ByteBuffer output)
            throws SSLException {
        int position = input.position();
        SSLEngineResult result;
        // Fixed issue #4
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) {
            // In Android 8.1, the BUFFER_OVERFLOW in the unwrap method will throw an
            // SSLException-EOFException. We catch this error and increase the output buffer
            // capacity.
            while (true) {
                int inputRemaining = input.remaining();
                try {
                    result = engine.unwrap(input, output);
                    break;
                } catch (SSLException e) {
                    if (!output.hasRemaining()) {
                        // Copy
                        ByteBuffer outputTemp = ByteBuffer.allocate(output.capacity() * 2);
                        output.flip();
                        outputTemp.put(output);
                        output = outputTemp;
                    } else {
                        // java.io.EOFException: Read error is an Android 8.1 system bug,
                        // it will cause #4 and #11. We swallowed this exception and not throw.
                        if ((e.getCause() instanceof EOFException && inputRemaining == 31 &&
                                input.remaining() == 0 && output.remaining() == output.capacity())) {
                            // Create a new SSLEngineResult.
                            result = new SSLEngineResult(SSLEngineResult.Status.OK,
                                    SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
                                    inputRemaining, 0);
                            break;
                        } else {
                            throw e;
                        }
                    }
                }
            }
        } else {
            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
                // Compat an Android 5.0 crash when calling
                // 'com.android.org.conscrypt.OpenSSLBIOSink.available()'
                try {
                    result = engine.unwrap(input, output);
                } catch (NullPointerException e) {
                    throw new SSLException(e);
                }
            } else {
                result = engine.unwrap(input, output);
            }
        }

        // This is a workaround for a bug in Android 5.0. Android 5.0 does not correctly update
        // the SSLEngineResult.bytesConsumed() in some cases and just return 0.
        //
        // See:
        //     - https://android-review.googlesource.com/c/platform/external/conscrypt/+/122080
        //     - https://github.com/netty/netty/issues/7758
        if (result.bytesConsumed() == 0) {
            int consumed = input.position() - position;
            if (consumed != result.bytesConsumed()) {
                // Create a new SSLEngineResult with the correct bytesConsumed().
                result = new SSLEngineResult(result.getStatus(), result.getHandshakeStatus(),
                        consumed, result.bytesProduced());
            }
        }
        return result;
    }
Try to encrypt a plaintext packet. If SSL handshake has finished, then encode it,
otherwise add it to queue and wait handshake finished.

@param buffer The plaintext packet.
@param callback A callback to observe actions and receive output packets.
@throws IOException If an I/O error has occurred.
netbare-core/src/main/java/com/github/megatronking/netbare/ssl/SSLCodec.java