/** Stops this <tt>PacketTransformer</tt>. */
 private synchronized void stop() {
   if (connectThread != null) connectThread = null;
   try {
     // The dtlsTransport and _srtpTransformer SHOULD be closed, of
     // course. The datagramTransport MUST be closed.
     if (dtlsTransport != null) {
       try {
         dtlsTransport.close();
       } catch (IOException ioe) {
         logger.error("Failed to (properly) close " + dtlsTransport.getClass(), ioe);
       }
       dtlsTransport = null;
     }
     if (_srtpTransformer != null) {
       _srtpTransformer.close();
       _srtpTransformer = null;
     }
   } finally {
     try {
       closeDatagramTransport();
     } finally {
       notifyAll();
     }
   }
 }
  /**
   * Sends the data contained in a specific byte array as application data through the DTLS
   * connection of this <tt>DtlsPacketTransformer</tt>.
   *
   * @param buf the byte array containing data to send.
   * @param off the offset in <tt>buf</tt> where the data begins.
   * @param len the length of data to send.
   */
  public void sendApplicationData(byte[] buf, int off, int len) {
    DTLSTransport dtlsTransport = this.dtlsTransport;
    Throwable throwable = null;

    if (dtlsTransport != null) {
      try {
        dtlsTransport.send(buf, off, len);
      } catch (IOException ioe) {
        throwable = ioe;
      }
    } else {
      throwable = new NullPointerException("dtlsTransport");
    }
    if (throwable != null) {
      // SrtpControl.start(MediaType) starts its associated
      // TransformEngine. We will use that mediaType to signal the normal
      // stop then as well i.e. we will ignore exception after the
      // procedure to stop this PacketTransformer has begun.
      if (mediaType != null && !tlsPeerHasRaisedCloseNotifyWarning) {
        logger.error("Failed to send application data over DTLS transport: ", throwable);
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public RawPacket reverseTransform(RawPacket pkt) {
    byte[] buf = pkt.getBuffer();
    int off = pkt.getOffset();
    int len = pkt.getLength();

    if (isDtlsRecord(buf, off, len)) {
      if (rtcpmux && Component.RTCP == componentID) {
        // This should never happen.
        logger.warn(
            "Dropping a DTLS record, because it was received on the"
                + " RTCP channel while rtcpmux is in use.");
        return null;
      }

      boolean receive;

      synchronized (this) {
        if (datagramTransport == null) {
          receive = false;
        } else {
          datagramTransport.queueReceive(buf, off, len);
          receive = true;
        }
      }
      if (receive) {
        DTLSTransport dtlsTransport = this.dtlsTransport;

        if (dtlsTransport == null) {
          // The specified pkt looks like a DTLS record and it has
          // been consumed for the purposes of the secure channel
          // represented by this PacketTransformer.
          pkt = null;
        } else {
          try {
            int receiveLimit = dtlsTransport.getReceiveLimit();
            int delta = receiveLimit - len;

            if (delta > 0) {
              pkt.grow(delta);
              buf = pkt.getBuffer();
              off = pkt.getOffset();
              len = pkt.getLength();
            } else if (delta < 0) {
              pkt.shrink(-delta);
              buf = pkt.getBuffer();
              off = pkt.getOffset();
              len = pkt.getLength();
            }

            int received = dtlsTransport.receive(buf, off, len, DTLS_TRANSPORT_RECEIVE_WAITMILLIS);

            if (received <= 0) {
              // No application data was decoded.
              pkt = null;
            } else {
              delta = len - received;
              if (delta > 0) pkt.shrink(delta);
            }
          } catch (IOException ioe) {
            pkt = null;
            // SrtpControl.start(MediaType) starts its associated
            // TransformEngine. We will use that mediaType to signal
            // the normal stop then as well i.e. we will ignore
            // exception after the procedure to stop this
            // PacketTransformer has begun.
            if (mediaType != null && !tlsPeerHasRaisedCloseNotifyWarning) {
              logger.error("Failed to decode a DTLS record!", ioe);
            }
          }
        }
      } else {
        // The specified pkt looks like a DTLS record but it is
        // unexpected in the current state of the secure channel
        // represented by this PacketTransformer. This PacketTransformer
        // has not been started (successfully) or has been closed.
        pkt = null;
      }
    } else if (transformEngine.isSrtpDisabled()) {
      // In pure DTLS mode only DTLS records pass through.
      pkt = null;
    } else {
      // DTLS-SRTP has not been initialized yet or has failed to
      // initialize.
      SinglePacketTransformer srtpTransformer = waitInitializeAndGetSRTPTransformer();

      if (srtpTransformer != null) pkt = srtpTransformer.reverseTransform(pkt);
      else if (DROP_UNENCRYPTED_PKTS) pkt = null;
      // XXX Else, it is our explicit policy to let the received packet
      // pass through and rely on the SrtpListener to notify the user that
      // the session is not secured.
    }
    return pkt;
  }