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);
    }
  }
  /** Shutdowns the service */
  public void shutdown() {
    Logger.log(getClass(), Logger.Tag.SHUTDOWN, "Shutdowning InviteService");
    if (done.compareAndSet(false, true)) {

      unSubscribeFromTransactionManager();
    }
    Logger.log(getClass(), Logger.Tag.SHUTDOWN, "InviteService shutdown successfully");
  }
  public void reject() throws RemoteException {
    Logger.log(TAG, "reject#started");

    acceptable.reject(dialog, StatusCode.TEMPORARY_UNAVAILABLE, "TEMPORARY_UNAVAILABLE");
    expire();

    Logger.log(TAG, "reject#finish");
  }
  public void accept() throws RemoteException {
    Logger.log(TAG, "accept#started");

    acceptable.accept(dialog);
    expire();

    Logger.log(TAG, "accept#finish");
  }
  /**
   * 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);
    }
  }
  /**
   * Handles server invite message
   *
   * @param msg - invite message
   */
  public void handleIncomingInvite(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 invite");
      // ClientIdentity localParty =
      // getStackContext().getStackClientRegistry().findAddressee(msg.getTo().getUriBuilder().getShortURI());
      ClientIdentity localParty = getStackContext().getClientRouter().findAddressee(msg);
      if (localParty != null) {
        assert getStackContext().getDialogStorage().findDialogForMessage(msg) == null;
        final Dialog dialog =
            getStackContext().getDialogStorage().getDialogForIncomingMessage(localParty, msg);
        TransactionType<InviteSrvTransaction, ? extends ServerCommonInviteTransaction>
            transactionType = SIP_INVITE_SERVER;

        doHandleIncomingInvite(msg, dialog, transactionType);
      } else {
        throw new DialogStateException(null, DialogStateException.Error.ADDRESSEE_NOT_FOUND, msg);
      }
    }
  }
  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");
  }
  public void onReceive(Context context, Intent intent) {
    Logger.log(TAG, "onReceive#" + intent + ", intent.getExtras()" + intent.getExtras());

    if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
      connectionManagerHandler.onConnectivity();
    } else if (ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED.equals(
        intent.getAction())) {
      connectionManagerHandler.onConnectivity();
    }
  }
  private void preAccept(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    Logger.log(TAG, "preAccept");

    if (!done.get()) {
      doPreAccept(dialog);
    }
  }
  /**
   * Cancels establishing DIALOG
   *
   * @param dialog - DIALOG to cancel
   */
  public void cancel(final Dialog dialog) {
    assert TransactionUtils.isTransactionExecutionThread()
        : "Code run in wrong thread. Must be run in TransactionThread. Now in "
            + Thread.currentThread();
    assert !done.get();
    Logger.log("Canceling call");

    if (!done.get()) {
      doCancel(dialog);
    }
  }
  /** 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");
  }
  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 doRunFinalization(
      final AtomicReference<TransactionResult<T>> transactionId,
      final boolean innerCall,
      final TransactionResult.Reason reason,
      final T transactionResult,
      final ContextCallback<T> callback) {

    repetitiousTaskManager.cancelTask(transactionId);

    synchronized (mutex) {

      // this code may be called from three points:
      // 1. On latch TIMEOUT. In that case first coming thread sets the default return value, runs
      // finalization routine and awaits it finishes.
      // 2. On shutdown call. In that case calling thread set the default return value, runs
      // finalization routine and returns.
      // 3. On release call. In that case calling thread set the return value equals to the value
      // passed by calling thread, runs finalization routine and returns.

      final boolean trnsIdEmpty =
          transactionId.compareAndSet(
              null,
              transactionResult == null
                  ? new TransactionResultImpl<T>(initialValue, reason)
                  : new TransactionResultImpl<T>(transactionResult, reason));

      if (trnsIdEmpty) {

        // flag whether we go through 1. scenario

        // create new finalization routine
        final FutureTask<Object> futureTask =
            new FutureTask<Object>(
                new Runnable() {
                  public void run() {
                    // run outer finalization routine
                    // Thread.currentThread().setName("Transaction finalizator");
                    try {
                      callback.finalizeTransaction(transactionId.get());
                    } catch (Exception e) {
                      e.printStackTrace();
                    } finally {
                      // let this iteration awaiting threads to be free and continue execution
                      cleanUpFinalization(transactionId);
                    }
                  }
                },
                null);

        // hold and execute new finalization routine
        finalizationMap.put(transactionId, futureTask);
        // TransactionUtils.getExecutorService().execute(futureTask);
        TransactionUtils.invokeLaterSmart(
            new TransactionRunnable("SynchronizationContext.doRunFinalization()") {
              public void run() {
                if (!futureTask.isCancelled() && !futureTask.isDone()) {
                  futureTask.run();
                  futureTask.cancel(true);
                }
              }
            });

        // set free waiting thread and initiate new iteration
        doReleaseThreads();
      }
    }

    // here one more synchronization point for awaiting threads from this iteration
    // this time threads waiting till the end of finalization routine.
    // All outer threads (shutdown(), release()) do not block here
    if (innerCall) {
      boolean needCleanupFinalization = false;

      // for all separate transaction we have separate transactionId.
      //noinspection SynchronizationOnLocalVariableOrMethodParameter
      synchronized (transactionId) {
        long startWait = System.currentTimeMillis();

        // waiting here until finalization task is done. Finalization task clears the map by itself
        // threads do not wait more then FINALIZATION_TIMEOUT*2
        while (finalizationMap.containsKey(transactionId)) {
          try {
            assert !TransactionUtils.isTransactionExecutionThread()
                : "Task queue blocking task detected";
            transactionId.wait(FINALIZATION_TIMEOUT);
          } catch (InterruptedException e) {
            Thread.interrupted();
          }

          if ((System.currentTimeMillis() - startWait) >= FINALIZATION_TIMEOUT) {
            needCleanupFinalization = true;
            break;
          }
        }
      }

      // if something happens to finalization routine (helper theread abruptly dead or something
      // like that) we make map clean up.
      // and try to notify all awaitng threads
      if (needCleanupFinalization) {
        Logger.log(
            "Warning!!! transaction finalization took  too long time. More than "
                + FINALIZATION_TIMEOUT
                + " millis");
        cleanUpFinalization(transactionId);
      }
    }
  }
  private TransactionResult<T> doAppend(final TimeoutUnit timeoutUnit, final boolean block) {
    Logger.log(
        "ReentrantSynchronizationContext",
        "doAppend#timeoutUnit = "
            + timeoutUnit
            + ", block = "
            + block
            + ", done.get() = "
            + done.get());
    TransactionResult<T> retValue = null;

    // possible to proceed only if there were no shutdown call on this instance
    if (!done.get()) {

      // latch for calling thread
      CountDownLatch localBarrier;

      // if the calling thread is a first on in this iteration the flag will get 'true' value
      boolean needInitiateTransaction;

      // each iteration has it's own instance of threadLocalTransactionId. Also it holds the current
      // iteration transaction value.
      // null means default value should be used
      final AtomicReference<TransactionResult<T>> threadLocalTransactionId;

      // nobody can go through if the same monitor is taken in other place. Probably in finalization
      // routine
      Logger.log("ReentrantSynchronizationContext", "doAppend#before sync");
      synchronized (mutex) {
        Logger.log("ReentrantSynchronizationContext", "doAppend#after sync");
        // each calling thread would have it's own latch.
        // It's supposed that some other awaking activity would countdown all the latches from list.
        localBarrier = new CountDownLatch(1);

        // list of all latches and associated threads. Used to release all threads at once for this
        // transaction iteration
        awaitingThreads.add(localBarrier);
        // barierList.add(localBarrier);

        // see description above
        needInitiateTransaction = false;
        if (transactionInProgress.compareAndSet(false, true)) {
          // if we first time in this iteration in this peace of code we mark that new transaction
          // shoud be started
          needInitiateTransaction = true;
          // also we prepare new transaction id. null means we don't have outer call to release()
          transactionId.set(new AtomicReference<TransactionResult<T>>(null));
        }
        // each thread must hold transaction id it it's own heap
        threadLocalTransactionId = transactionId.get();
      }

      // call outer initialization routine
      if (needInitiateTransaction) {
        Logger.log("ReentrantSynchronizationContext", "initiateTransaction#start");
        callback.initiateTransaction();
        Logger.log("ReentrantSynchronizationContext", "initiateTransaction#end");
      }

      if (block) {
        TransactionResult.Reason reason = TransactionResult.Reason.OUTER_INTERRUPT;
        try {
          // make thread to wait until transaction finished or TIMEOUT EXPIRES
          if (timeoutUnit != null) {
            Logger.log(
                "ReentrantSynchronizationContext", "start await, timeoutUnit = " + timeoutUnit);
            localBarrier.await(
                timeoutUnit.getTimeout(),
                timeoutUnit.getTimeoutUnit() == null
                    ? TimeUnit.MILLISECONDS
                    : timeoutUnit.getTimeoutUnit());
          } else {
            localBarrier.await();
          }

          reason =
              localBarrier.getCount() > 0
                  ? TransactionResult.Reason.TIMEOUT
                  : TransactionResult.Reason.OUTER_INTERRUPT;

          Logger.log("ReentrantSynchronizationContext", "end await, reason = " + reason);
        } catch (InterruptedException e) {
          // set thread flag
          Thread.interrupted();
          reason = TransactionResult.Reason.OUTER_INTERRUPT;
        } finally {
          // try to run finalization routine
          final TransactionResult<T> transactionResult = threadLocalTransactionId.get();
          Logger.log("ReentrantSynchronizationContext", "start tr. finalization");
          runFinalization(
              threadLocalTransactionId,
              true,
              reason,
              transactionResult == null ? null : transactionResult.getValue(),
              callback);
          Logger.log("ReentrantSynchronizationContext", "end tr. finalization");
          retValue = threadLocalTransactionId.get();
          Logger.log(
              "ReentrantSynchronizationContext", "start tr. finalization, retValue = " + retValue);
        }
      } else {
        if (needInitiateTransaction) {
          boolean needTimeout =
              !(timeoutUnit == null
                  || timeoutUnit.getTimeout() == null
                  || timeoutUnit.getTimeoutUnit() == null);

          if (needTimeout) {
            long timeoutInMillis = timeoutUnit.getTimeoutUnit().toMillis(timeoutUnit.getTimeout());

            repetitiousTaskManager.startDelayedTask(
                threadLocalTransactionId,
                new RepetitiousTaskManager.Repeater<AtomicReference<TransactionResult<T>>>() {
                  @Override
                  public void onRepeat(
                      AtomicReference<TransactionResult<T>> key, Shutdownable shutdownable) {
                    Logger.log(TAG, String.format("onRepeat"));
                    final TransactionResult<T> transactionResult = key.get();
                    runFinalization(
                        key,
                        true,
                        TransactionResult.Reason.TIMEOUT,
                        transactionResult == null ? null : transactionResult.getValue(),
                        callback);
                    shutdownable.shutdown();
                  }
                },
                timeoutInMillis);
          }
        }
      }
    } else {
      throw new IllegalStateException("Context already shutdown.");
    }

    return retValue;
  }