public void handleIncomingUpdate(Request msg) throws DialogStateException {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (!done.get()) {
      Logger.log("Remote party has sent update");

      final Dialog dialog = getStackContext().getDialogStorage().findDialogForMessage(msg);
      assert dialog != null;

      checkUpdatePreconditions(dialog, msg);

      Logger.log(TAG, "mark dialog as update in progress");
      dialog.markUpdateInProgress(InitiateParty.REMOTE);

      // TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
      // transactionType = SIP_REINVITE_SERVER;
      TransactionType<UpdateSrvTransaction, UpdateServerTransaction> transactionType =
          SIP_UPDATE_SERVER;

      doHandleIncomingUpdate(msg, dialog, transactionType);
    }
  }
  /**
   * Handles server noninvite message
   *
   * @param msg - noninvite message
   */
  public void handleIncomingBye(final Request msg) {

    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert msg != null && MessageType.SIP_BYE == MessageType.parse(msg.getMethod());

    Logger.log("Remote party has sent noninvite");
    if (!done.get()) {

      final Dialog dialog = getStackContext().getDialogStorage().findDialogForMessage(msg);
      assert dialog != null;
      assert STATED == dialog.getState();

      dialog.getMessageHistory().addMessage(msg, true);

      final TransactionManager transactionManager = getTransactionManager();
      transactionManager.addListener(
          new FirstMessageResolver(SIP_BYE_SERVER.getName(), dialog, msg, transactionManager));

      final Transaction transaction =
          transactionManager.lookUpTransaction(dialog, null, SIP_BYE_SERVER);
      runAsynchronously(transaction, TRANSACTION_TIMEOUT);
    }
  }
        public void onTransactionCreate(final TransactionBuildUpEvent<BaseSipMessage> event) {
          assert SIP_REINVITE_CLIENT == event.getTransaction().getTransactionType();

          Dialog dialog = (Dialog) event.getEntity();
          final Transaction<Boolean, BaseSipMessage> transaction = event.getTransaction();

          // DIALOG.putCustomParameter(Dialog.ParamKey.REINVITE_IN_PROGRESS, Boolean.TRUE);
          dialog.markReInviteInProgress(InitiateParty.LOCAL);

          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(new AuthChallengeListener<BaseSipMessage>(transaction, dialog));
          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(
              new MiddleManForClientMessageBuildingSupport<BaseSipMessage>(transaction, dialog));
          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(
              new ReInviteStateMiddleMan<BaseSipMessage>(
                  transaction, dialog, dialogStateListenerHolder) {

                protected void onDialogCleanUp(final Dialog dialog) {
                  getStackContext().getDialogStorage().cleanUpDialog(dialog);
                }
              });
          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(
              new ReinviteInProgressListener<BaseSipMessage>(
                  transaction, dialog, InitiateParty.LOCAL));
        }
  private void checkUpdatePreconditions(final Dialog dialog, final Request msg)
      throws DialogStateException {
    if (STATED == dialog.getState()) {
      throw new DialogStateException(
          dialog,
          UPDATE_FOR_STATED_DIALOG,
          msg,
          "Can not update DIALOG. Dialog is stated already. ");
    }
    if (dialog.isUpdateInProgress()) {
      throw new DialogStateException(
          dialog,
          UPDATE_DURING_PREVIOUS_UPDATE,
          msg,
          "Can not update DIALOG. Previous update still in progress.");
    }

    /**
     * A UAS that receives an UPDATE before it has generated a final response to a previous UPDATE
     * on the same dialog MUST return a 500 response to the new UPDATE, and MUST include a
     * Retry-After HEADER field with a randomly chosen value between 0 and 10 seconds.
     *
     * <p>If an UPDATE is received that contains an offer, and the UAS has generated an offer (in an
     * UPDATE, PRACK or INVITE) to which it has not yet received an answer, the UAS MUST reject the
     * UPDATE with a 491 response. Similarly, if an UPDATE is received that contains an offer, and
     * the UAS has received an offer (in an UPDATE, PRACK, or INVITE) to which it has not yet
     * generated an answer, the UAS MUST reject the UPDATE with a 500 response, and MUST include a
     * Retry-After HEADER field with a randomly chosen value between 0 and 10 seconds.
     *
     * <p>If a UA receives an UPDATE for an existing dialog, it MUST check any version identifiers
     * in the SESSION description or, if there are no version identifiers, the content of the
     * SESSION description to see if it has changed. If the SESSION description has changed, the UAS
     * MUST adjust the SESSION parameters accordingly and generate an answer in the 2xx response.
     * However, unlike a re-INVITE, the UPDATE MUST be responded to promptly, and therefore the USER
     * cannot generally be prompted to approve the SESSION changes. If the UAS cannot change the
     * SESSION parameters without prompting the USER, it SHOULD reject the request with a 504
     * response. If the new SESSION description is not acceptable, the UAS can reject it by
     * returning a 488 (Not Acceptable Here) response for the UPDATE. This response SHOULD include a
     * Warning HEADER field.
     *
     * <p>If a UAC receives a 491 response to a UPDATE, it SHOULD start a timer with a value T
     * chosen as follows:
     *
     * <p>1. If the UAC is the owner of the Call-ID of the dialog ID (meaning it generated the
     * value), T has a randomly chosen value between 2.1 and 4 seconds in units of 10 ms.
     *
     * <p>2. If the UAC is not the owner of the Call-ID of the dialog ID, T has a randomly chosen
     * value between 0 and 2 seconds in units of 10 ms.
     *
     * <p>When the timer fires, the UAC SHOULD attempt the UPDATE once more, if it still desires for
     * that SESSION modification to take place. For example, if the call was already hung up with a
     * BYE, the UPDATE would not take place.
     */
  }
  /**
   * This method initiates SIP_BYE for established DIALOG
   *
   * @param dialog - DIALOG to terminate
   */
  public void bye(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (!done.get()) {
      assert STATED == dialog.getState()
          : "Wrong dialog state. Must be " + STATED + " now is " + dialog.getState();
      doBye(dialog, TRANSACTION_TIMEOUT);
    }
  }
        public void onTransactionCreate(final TransactionBuildUpEvent<BaseSipMessage> event) {
          assert SIP_REINVITE_SERVER == event.getTransaction().getTransactionType();

          Dialog dialog = (Dialog) event.getEntity();
          final Transaction<Boolean, BaseSipMessage> transaction = event.getTransaction();

          // DIALOG.putCustomParameter(Dialog.ParamKey.REINVITE_IN_PROGRESS, Boolean.TRUE);
          // TODo reinvire is used for update too
          // dialog.markReInviteInProgress(InitiateParty.REMOTE);

          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(
              new IncomingReInviteListener<BaseSipMessage>(
                  dialog,
                  createSafeInviteAcceptable(dialog),
                  transaction,
                  incomingCallListenerHolder));

          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(
              new MiddleManForServerMessageBuildingSupport(transaction, dialog));
          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(
              new ReInviteStateMiddleMan<BaseSipMessage>(
                  transaction, dialog, dialogStateListenerHolder) {

                protected void onDialogCleanUp(final Dialog dialog) {
                  getStackContext().getDialogStorage().cleanUpDialog(dialog);
                }
              });
          // listener will un-subscribe automatically on transaction complete
          // TODO change
          if (dialog.isUpdateInProgress()) {
            transaction.addListener(
                new UpdateInProgressListener<BaseSipMessage>(
                    transaction, dialog, InitiateParty.REMOTE));
          } else {
            transaction.addListener(
                new ReinviteInProgressListener<BaseSipMessage>(
                    transaction, dialog, InitiateParty.REMOTE));
          }
          // listener will un-subscribe automatically on transaction complete
          transaction.addListener(
              new DialogCleanUpListener<BaseSipMessage>(transaction, dialog, false) {

                protected void onDialogCleanUp(final Dialog dialog) {
                  getStackContext().getDialogStorage().cleanUpDialog(dialog);
                }
              });
        }
  public void update(Dialog dialog) throws DialogStateException {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert getStackContext().getDialogStorage().findDialogByCallId(dialog.getCallId()) != null
        : "DIALOG being updated is already  terminated";

    if (!done.get()) {

      checkUpdatePreconditions(dialog, null);
      dialog
          .getOutgoingSdpMessage()
          .setSessionVersion(dialog.getOutgoingSdpMessage().getSessionVersion() + 1);
      doUpdate(dialog);
    }
  }
 private void checkReInvitePreconditions(final Dialog dialog, final Request msg)
     throws DialogStateException {
   if (EARLY == dialog.getState()) {
     throw new DialogStateException(
         dialog,
         REINVITE_FOR_EARLY_DIALOG,
         msg,
         "Can not update (reinvite) DIALOG. Dialog is not stated yet. Dialog  state is "
             + dialog.getState());
   }
   if (dialog.isReInviteInProgress()) {
     throw new DialogStateException(
         dialog,
         REINVITE_DURING_PREVIOUS_REINVITE,
         msg,
         "Can not update (reinvite) DIALOG. Previous reinvite still in progress.");
   }
 }
  private void doHandleIncomingUpdate(
      Request msg,
      Dialog dialog,
      TransactionType<UpdateSrvTransaction, UpdateServerTransaction> transactionType) {

    dialog.getMessageHistory().addMessage(msg, true);
    SdpMessage sdp = SipMessageUtils.getSdpFromMessage(msg);
    if (sdp != null) {
      dialog.setIncomingSdpMessage(sdp);
    }

    final TransactionManager transactionManager = getTransactionManager();
    transactionManager.addListener(
        new FirstMessageResolver(transactionType.getName(), dialog, msg, transactionManager));

    final UpdateSrvTransaction transaction =
        transactionManager.lookUpTransaction(dialog, null, transactionType);
    runAsynchronously((Transaction<Boolean, BaseSipMessage>) transaction);
  }
  public void reInvite(final Dialog dialog) throws DialogStateException {
    Logger.log(TAG, "reInvite#started");
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert getStackContext().getDialogStorage().findDialogByCallId(dialog.getCallId()) != null
        : "DIALOG being re-invited is already  terminated";

    if (!done.get()) {

      checkReInvitePreconditions(dialog, null);
      dialog
          .getOutgoingSdpMessage()
          .setSessionVersion(dialog.getOutgoingSdpMessage().getSessionVersion() + 1);
      doReInvite(dialog, LONG_TRANSACTION_TIMEOUT);
    }
    Logger.log(TAG, "reInvite#finished");
  }
  private void doUpdate(Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (dialog.getInitiateParty() == InitiateParty.LOCAL) {
      getTransactionManager().findTransaction(dialog, SIP_INVITE_CLIENT).update();
    } else {
      getTransactionManager().findTransaction(dialog, SIP_INVITE_SERVER).update();
    }
  }
  public void handleIncomingCancel(Request msg) throws DialogStateException {
    assert !done.get();
    assert msg != null && MessageType.SIP_CANCEL == MessageType.parse(msg.getMethod());

    if (!done.get()) {
      Logger.log("Remote party has sent SIP_CANCEL");

      final Dialog dialog = getStackContext().getDialogStorage().findDialogForMessage(msg);

      if (dialog != null) {
        dialog.getMessageHistory().addMessage(msg, true);

        final InviteSrvTransaction transaction =
            getTransactionManager().findTransaction(dialog, SIP_INVITE_SERVER);

        if (transaction != null) {
          final DialogStateEvent<BaseSipMessage> stateEvent =
              new DefaultDialogStateEvent<BaseSipMessage>(
                  dialog, SessionState.SESSION_TERMINATED, msg);

          dialogStateListenerHolder.getNotifier().onSessionTerminated(stateEvent);
          dialogStateListenerHolder.getNotifier().onSessionEnded(stateEvent);
          // DIALOG.putCustomParameter(ParamKey.INITIAL_MESSAGE, ((Transaction)
          // transaction).getInitialMessage());

          transaction.cancel();
        } else {
          // assert false : "Transaction already terminated for msg: " + msg.shortDescription() + "
          // dialog: " + dialog;
          throw new DialogStateException(
              dialog, DialogStateException.Error.REQUEST_FOR_UNKNOWN_DIALOG, msg);
        }
      } else {
        // assert false : "Dialog is already terminated or never exist. Message :" +
        // msg.shortDescription();
        throw new DialogStateException(
            dialog, DialogStateException.Error.REQUEST_FOR_UNKNOWN_DIALOG, msg);
      }
    }
  }
  private void doHandleIncomingInvite(
      final Request msg,
      final Dialog dialog,
      final TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
          transactionType) {

    dialog.getMessageHistory().addMessage(msg, true);
    // DIALOG.putCustomParameter(ParamKey.LAST_MESSAGE, msg);
    Logger.log("doHandleIncomingInvite", "");
    SdpMessage sdp = SipMessageUtils.getSdpFromMessage(msg);
    if (sdp != null) {
      dialog.setIncomingSdpMessage(sdp);
    }

    final TransactionManager transactionManager = getTransactionManager();
    transactionManager.addListener(
        new FirstMessageResolver(transactionType.getName(), dialog, msg, transactionManager));

    final InviteSrvTransaction transaction =
        transactionManager.lookUpTransaction(dialog, null, transactionType);
    runAsynchronously((Transaction<Boolean, BaseSipMessage>) transaction);
  }
  public void handleIncomingReInvite(final Request msg) throws DialogStateException {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    assert msg != null && MessageType.SIP_INVITE == MessageType.parse(msg.getMethod());

    if (!done.get()) {
      Logger.log("Remote party has sent ReInvite");

      final Dialog dialog = getStackContext().getDialogStorage().findDialogForMessage(msg);
      assert dialog != null;

      checkReInvitePreconditions(dialog, msg);
      dialog.markReInviteInProgress(InitiateParty.REMOTE);

      TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
          transactionType = SIP_REINVITE_SERVER;

      doHandleIncomingInvite(msg, dialog, transactionType);
    }
  }
  private void doAcceptUpdate(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (dialog.isReInviteInProgress() || dialog.isUpdateInProgress()) {
      // transactionType = SIP_REINVITE_SERVER;
      TransactionType<UpdateSrvTransaction, UpdateServerTransaction> transactionType =
          SIP_UPDATE_SERVER;
      getTransactionManager().findTransaction(dialog, transactionType).accept();
    } else {
      TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
          transactionType = SIP_INVITE_SERVER;
      getTransactionManager().findTransaction(dialog, transactionType).acceptUpdate();
    }

    /*        if (dialog.getInitiateParty() == InitiateParty.LOCAL) {
                getTransactionManager().findTransaction(dialog, SIP_INVITE_CLIENT).acceptUpdate();
            }
            else {
                getTransactionManager().findTransaction(dialog, SIP_INVITE_SERVER).acceptUpdate();
            }
    */ }
  private void doPreAccept(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction> transactionType;

    if (dialog.isReInviteInProgress()) {
      transactionType = SIP_REINVITE_SERVER;
    } else {
      transactionType = SIP_INVITE_SERVER;
    }

    getTransactionManager().findTransaction(dialog, transactionType).preAccept();
  }
  /**
   * Rejects server invite
   *
   * @param dialog - associated DIALOG
   * @param statusCode - status code to send to remote party
   * @param alternativeUserAddress - alternative USER address for status code 302(Moved)
   */
  private void doReject(
      final Dialog dialog, final int statusCode, final String alternativeUserAddress) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction> transactionType;
    if (dialog.isReInviteInProgress()) {
      transactionType = SIP_REINVITE_SERVER;
    } else {
      transactionType = SIP_INVITE_SERVER;
    }

    assert transactionType == SIP_INVITE_SERVER || statusCode == 488;

    getTransactionManager()
        .findTransaction(dialog, transactionType)
        .reject(statusCode, alternativeUserAddress);
  }
  private void doRejectUpdate(
      final Dialog dialog, final int statusCode, final String alternativeUserAddress) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (dialog.getInitiateParty() == InitiateParty.LOCAL) {
      getTransactionManager()
          .findTransaction(dialog, SIP_INVITE_CLIENT)
          .rejectUpdate(statusCode, alternativeUserAddress);
    } else {
      // getTransactionManager().findTransaction(dialog, SIP_INVITE_SERVER).rejectUpdate(statusCode,
      // alternativeUserAddress);
      TransactionType<UpdateSrvTransaction, UpdateServerTransaction> transactionType =
          SIP_UPDATE_SERVER;
      getTransactionManager()
          .findTransaction(dialog, transactionType)
          .reject(statusCode, alternativeUserAddress);
    }
  }
  /** This method tries to invite remote party */
  public void invite(final Dialog dialog) throws DialogStateException {
    Logger.log(TAG, "invite#started");
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();

    if (!done.get()) {
      if (STATED == dialog.getState()) {
        throw new DialogStateException(
            dialog,
            INVITE_FOR_STATED_DIALOG,
            null,
            "Can not invite remote party. Dialog is already stated.");
      }

      if (getStackContext().getConfig().useInviteRefresh()) {
        addDialogStateListener(dialog, listenerForInviteRefresh);
      }
      doInvite(dialog, LONG_TRANSACTION_TIMEOUT);
    }
    Logger.log(TAG, "invite#finished");
  }