@Override
  public void fromApp(final SipResponse res, final TransactionListener listener) {

    Preconditions.checkNotNull(res);

    if ((res.getStatus().getCode() / 100) == 1) {
      switch (this.state) {
        case Trying:
          this.response = res;
          this.setState(State.Proceeding);
          listener.sendToNetwork(this.flowId, this.response);
          break;
        case Proceeding:
          this.response = res;
          listener.sendToNetwork(this.flowId, this.response);
          break;
        case Initial:
        case Completed:
        case Terminated:
          break;
      }
    } else if ((res.getStatus().getCode() / 100) >= 2) {

      // schedule the expiry timer.
      // TODO change to dynamic value.
      listener.schedule(this.getBranchId(), Duration.ofSeconds(32));

      switch (this.state) {
        case Trying:
          this.response = res;
          this.setState(State.Completed);
          listener.sendToNetwork(this.flowId, this.response);
          break;
        case Proceeding:
          this.response = res;
          this.setState(State.Completed);
          listener.sendToNetwork(this.flowId, this.response);
          break;
        case Initial:
        case Completed:
        case Terminated:
          break;
      }
    }
  }
  @Override
  public void fromNetwork(final SipRequestReceivedEvent e, final TransactionListener listener) {

    switch (this.state) {
      case Initial:
        // pass the message on to the app layer.
        this.flowId = e.getFlowId();
        this.setState(State.Trying);
        listener.sendToApp(null, e.getMessage());
        break;
      case Trying:
        break;
      case Proceeding:
      case Completed:
        // always send the response down the same flow as we received it.
        Preconditions.checkNotNull(this.response);
        listener.sendToNetwork(e.getFlowId(), this.response);
        break;
      case Terminated:
        break;
    }
  }