/** "Remove matching deferred transmission entries." [DTN2] */
  void remove_from_deferred(final Bundle bundle, int actions) {

    ContactManager cm = BundleDaemon.getInstance().contactmgr();

    cm.get_lock().lock();
    try {
      final LinkSet links = cm.links();
      Iterator<Link> iter = links.iterator();

      while (iter.hasNext()) {
        Link link = iter.next();

        // "a bundle might be deleted immediately after being loaded
        // from storage, meaning that remove_from_deferred is called
        // before the deferred list is created (since the link isn't
        // fully set up yet). so just skip the link if there's no
        // router info, and therefore no deferred list" [DTN2]
        if (link.router_info() == null) {
          continue;
        }

        DeferredList deferred = deferred_list(link);
        ForwardingInfo info = deferred.find(bundle);
        if (info != null) {
          if ((info.action().getCode() & actions) > 0) {
            Log.d(
                TAG, String.format("removing bundle %s from link %s deferred list", bundle, link));
            deferred.del(bundle);
          }
        }
      }
    } finally {
      cm.get_lock().unlock();
    }
  }
  /**
   * "Called when the next hop link is available for transmission (i.e. either when it first arrives
   * and the contact is brought up or when a bundle is completed and it's no longer busy).
   *
   * <p>Loops through the bundle list and calls fwd_to_matching on all bundles." [DTN2]
   */
  protected void check_next_hop(Link next_hop) {

    // "if the link isn't open, there's nothing to do now" [DTN2]
    if (!next_hop.isopen()) {
      Log.d(
          TAG,
          String.format(
              "check_next_hop %s -> %s: link not open...", next_hop.name(), next_hop.nexthop()));
      return;
    }

    // "if the link queue doesn't have space (based on the low water
    // mark) don't do anything" [DTN2]
    if (!next_hop.queue_has_space()) {
      Log.d(
          TAG,
          String.format(
              "check_next_hop %s -> %s: no space in queue...",
              next_hop.name(), next_hop.nexthop()));
      return;
    }

    Log.d(
        TAG,
        String.format(
            "check_next_hop %s -> %s: checking deferred bundle list...",
            next_hop.name(), next_hop.nexthop()));

    // "because the loop below will remove the current bundle from
    // the deferred list, invalidating any iterators pointing to its
    // position, make sure to advance the iterator before processing
    // the current bundle" [DTN2]
    DeferredList deferred = deferred_list(next_hop);

    deferred.list().get_lock().lock();
    try {

      Iterator<Bundle> iter = deferred.list().begin();
      while (iter.hasNext()) {
        if (next_hop.queue_is_full()) {
          Log.d(
              TAG,
              String.format(
                  "check_next_hop %s: link queue is full, stopping loop", next_hop.name()));
          break;
        }

        Bundle bundle = iter.next();

        ForwardingInfo info = deferred.find(bundle);
        assert info != null
            : "TableBasedRouter: check_next_hop, ForwardingInfo regarding Bundle is null";

        // "if should_fwd returns false, then the bundle was either
        // already transmitted or is in flight on another node. since
        // it's possible that one of the other transmissions will
        // fail, we leave it on the deferred list for now, relying on
        // the transmitted handlers to clean up the state" [DTN2]
        if (!this.should_fwd(bundle, next_hop, info.action())) {
          Log.d(TAG, String.format("check_next_hop: not forwarding to link %s", next_hop.name()));
          continue;
        }

        // "if the link is available and not open, open it" [DTN2]
        if (next_hop.isNotUnavailable() && (!next_hop.isopen()) && (!next_hop.isopening())) {
          Log.d(
              TAG,
              String.format(
                  "check_next_hop: " + "opening %s because a message is intended for it",
                  next_hop.name()));
          actions_.open_link(next_hop);
        }

        // "remove the bundle from the deferred list" [DTN2]
        Log.d(
            TAG,
            String.format(
                "check_next_hop: sending bundle %d to %s", bundle.bundleid(), next_hop.name()));

        iter.remove();
        actions_.queue_bundle(bundle, next_hop, info.action(), info.custody_spec());
      }
    } catch (BundleListLockNotHoldByCurrentThread e) {
      Log.e(TAG, "Table Based Router " + e.toString());
    } finally {
      deferred.list().get_lock().unlock();
    }
  }