@Override
    SSLEngineResult unwrap(final ByteBuffer[] srcs, int srcsOffset, final int srcsLength,
            final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength)
            throws SSLException {
        checkArgument(srcs != null, "srcs is null");
        checkArgument(dsts != null, "dsts is null");
        checkPositionIndexes(srcsOffset, srcsOffset + srcsLength, srcs.length);
        checkPositionIndexes(dstsOffset, dstsOffset + dstsLength, dsts.length);

        // Determine the output capacity.
        final int dstLength = calcDstsLength(dsts, dstsOffset, dstsLength);
        final int endOffset = dstsOffset + dstsLength;

        final int srcsEndOffset = srcsOffset + srcsLength;
        final long srcLength = calcSrcsLength(srcs, srcsOffset, srcsEndOffset);

        synchronized (ssl) {
            switch (state) {
                case STATE_MODE_SET:
                    // Begin the handshake implicitly.
                    beginHandshakeInternal();
                    break;
                case STATE_CLOSED_INBOUND:
                case STATE_CLOSED:
                    freeIfDone();
                    // If the inbound direction is closed. we can't send anymore.
                    return new SSLEngineResult(Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
                case STATE_NEW:
                    throw new IllegalStateException(
                            "Client/server mode must be set before calling unwrap");
                default:
                    break;
            }

            HandshakeStatus handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
            if (!handshakeFinished) {
                handshakeStatus = handshake();
                if (handshakeStatus == NEED_WRAP) {
                    return NEED_WRAP_OK;
                }
                if (state == STATE_CLOSED) {
                    return NEED_WRAP_CLOSED;
                }
                // NEED_UNWRAP - just fall through to perform the unwrap.
            }

            // Consume any source data. Skip this if there are unread cleartext data.
            boolean noCleartextDataAvailable = pendingInboundCleartextBytes() <= 0;
            int lenRemaining = 0;
            if (srcLength > 0 && noCleartextDataAvailable) {
                if (srcLength < SSL3_RT_HEADER_LENGTH) {
                    // Need to be able to read a full TLS header.
                    return new SSLEngineResult(BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
                }

                int packetLength = SSLUtils.getEncryptedPacketLength(srcs, srcsOffset);
                if (packetLength < 0) {
                    throw new SSLException("Unable to parse TLS packet header");
                }

                if (srcLength < packetLength) {
                    // We either have not enough data to read the packet header or not enough for
                    // reading the whole packet.
                    return new SSLEngineResult(BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
                }

                // Limit the amount of data to be read to a single packet.
                lenRemaining = packetLength;
            } else if (noCleartextDataAvailable) {
                // No pending data and nothing provided as input.  Need more data.
                return new SSLEngineResult(BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
            }

            // Write all of the encrypted source data to the networkBio
            int bytesConsumed = 0;
            if (lenRemaining > 0 && srcsOffset < srcsEndOffset) {
                do {
                    ByteBuffer src = srcs[srcsOffset];
                    int remaining = src.remaining();
                    if (remaining == 0) {
                        // We must skip empty buffers as BIO_write will return 0 if asked to
                        // write something with length 0.
                        srcsOffset++;
                        continue;
                    }
                    // Write the source encrypted data to the networkBio.
                    int written = writeEncryptedData(src, min(lenRemaining, remaining));
                    if (written > 0) {
                        bytesConsumed += written;
                        lenRemaining -= written;
                        if (lenRemaining == 0) {
                            // A whole packet has been consumed.
                            break;
                        }

                        if (written == remaining) {
                            srcsOffset++;
                        } else {
                            // We were not able to write everything into the BIO so break the
                            // write loop as otherwise we will produce an error on the next
                            // write attempt, which will trigger a SSL.clearError() later.
                            break;
                        }
                    } else {
                        // BIO_write returned a negative or zero number, this means we could not
                        // complete the write operation and should retry later.
                        // We ignore BIO_* errors here as we use in memory BIO anyway and will
                        // do another SSL_* call later on in which we will produce an exception
                        // in case of an error
                        NativeCrypto.SSL_clear_error();
                        break;
                    }
                } while (srcsOffset < srcsEndOffset);
            }

            // Now read any available plaintext data.
            int bytesProduced = 0;
            try {
                if (dstLength > 0) {
                    // Write decrypted data to dsts buffers
                    for (int idx = dstsOffset; idx < endOffset; ++idx) {
                        ByteBuffer dst = dsts[idx];
                        if (!dst.hasRemaining()) {
                            continue;
                        }

                        int bytesRead = readPlaintextData(dst);
                        if (bytesRead > 0) {
                            bytesProduced += bytesRead;
                            if (dst.hasRemaining()) {
                                // We haven't filled this buffer fully, break out of the loop
                                // and determine the correct response status below.
                                break;
                            }
                        } else {
                            switch (bytesRead) {
                                case -SSL_ERROR_WANT_READ:
                                case -SSL_ERROR_WANT_WRITE: {
                                    return newResult(bytesConsumed, bytesProduced, handshakeStatus);
                                }
                                case -SSL_ERROR_ZERO_RETURN: {
                                    // We received a close_notify from the peer, so mark the
                                    // inbound direction as closed and shut down the SSL object
                                    closeAll();
                                    return new SSLEngineResult(Status.CLOSED,
                                            pendingOutboundEncryptedBytes() > 0
                                                    ? NEED_WRAP : NOT_HANDSHAKING,
                                            bytesConsumed, bytesProduced);
                                }
                                default: {
                                    // Should never get here.
                                    closeAll();
                                    throw newSslExceptionWithMessage("SSL_read");
                                }
                            }
                        }
                    }
                } else {
                    // If the capacity of all destination buffers is 0 we need to trigger a SSL_read
                    // anyway to ensure everything is flushed in the BIO pair and so we can detect
                    // it in the pendingInboundCleartextBytes() call.
                    ssl.forceRead();
                }
            } catch (InterruptedIOException e) {
                return newResult(bytesConsumed, bytesProduced, handshakeStatus);
            } catch (IOException e) {
                // Shut down the SSL and rethrow the exception.  Users will need to drain any alerts
                // from the SSL before closing.
                closeAll();
                throw convertException(e);
            }

            // There won't be any application data until we're done handshaking.
            // We first check handshakeFinished to eliminate the overhead of extra JNI call if
            // possible.
            int pendingCleartextBytes = handshakeFinished ? pendingInboundCleartextBytes() : 0;
            if (pendingCleartextBytes > 0) {
                // We filled all buffers but there is still some data pending in the BIO buffer,
                // return BUFFER_OVERFLOW.
                return new SSLEngineResult(BUFFER_OVERFLOW,
                        mayFinishHandshake(handshakeStatus == FINISHED
                                        ? handshakeStatus
                                        : getHandshakeStatusInternal()),
                        bytesConsumed, bytesProduced);
            }

            return newResult(bytesConsumed, bytesProduced, handshakeStatus);
        }
    }
Work-around to allow this method to be called on older versions of Android.
common/src/main/java/org/conscrypt/ConscryptEngine.java