/**
   * 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);
    }
  }
  /**
   * 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);
    }
  }
 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 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 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");
  }