/**
   * Sends local candidate addresses from the local peer to the remote peer using the
   * <tt>candidates</tt> {@link SessionIQ}.
   *
   * @param candidates the local candidate addresses to be sent from the local peer to the remote
   *     peer using the <tt>candidates</tt> {@link SessionIQ}
   */
  protected void sendCandidates(Iterable<GTalkCandidatePacketExtension> candidates) {
    ProtocolProviderServiceJabberImpl protocolProvider = getProtocolProvider();

    SessionIQ candidatesIQ = new SessionIQ();

    candidatesIQ.setGTalkType(GTalkType.CANDIDATES);
    candidatesIQ.setFrom(protocolProvider.getOurJID());
    candidatesIQ.setInitiator(isInitiator() ? getAddress() : protocolProvider.getOurJID());
    candidatesIQ.setID(getSID());
    candidatesIQ.setTo(getAddress());
    candidatesIQ.setType(IQ.Type.SET);

    for (GTalkCandidatePacketExtension candidate : candidates) {
      // Android phone and Google Talk client does not seems to like IPv6
      // candidates since it reject the IQ candidates with an error
      // so do not send IPv6 candidates to Android phone or Talk client
      if (isAndroidOrVtokOrTalkClient(getAddress())
          && NetworkUtils.isIPv6Address(candidate.getAddress())) continue;

      candidatesIQ.addExtension(candidate);
    }

    protocolProvider.getConnection().sendPacket(candidatesIQ);
  }
  /**
   * Sends enable or disable carbon packet to the server.
   *
   * @param enable if <tt>true</tt> sends enable packet otherwise sends disable packet.
   */
  private void enableDisableCarbon(final boolean enable) {
    IQ iq =
        new IQ() {

          @Override
          public String getChildElementXML() {
            return "<" + (enable ? "enable" : "disable") + " xmlns='urn:xmpp:carbons:2' />";
          }
        };

    Packet response = null;
    try {
      PacketCollector packetCollector =
          jabberProvider
              .getConnection()
              .createPacketCollector(new PacketIDFilter(iq.getPacketID()));
      iq.setFrom(jabberProvider.getOurJID());
      iq.setType(IQ.Type.SET);
      jabberProvider.getConnection().sendPacket(iq);
      response = packetCollector.nextResult(SmackConfiguration.getPacketReplyTimeout());

      packetCollector.cancel();
    } catch (Exception e) {
      logger.error("Failed to enable carbon.", e);
    }

    isCarbonEnabled = false;

    if (response == null) {
      logger.error("Failed to enable carbon. No response is received.");
    } else if (response.getError() != null) {
      logger.error("Failed to enable carbon: " + response.getError());
    } else if (!(response instanceof IQ) || !((IQ) response).getType().equals(IQ.Type.RESULT)) {
      logger.error("Failed to enable carbon. The response is not correct.");
    } else {
      isCarbonEnabled = true;
    }
  }
  /**
   * Initiate a Google Talk session {@link SessionIQ}.
   *
   * @param sessionInitiateExtensions a collection of additional and optional
   *     <tt>PacketExtension</tt>s to be added to the <tt>initiate</tt> {@link SessionIQ} which is
   *     to initiate the session with this <tt>CallPeerGTalkImpl</tt>
   * @throws OperationFailedException exception
   */
  protected synchronized void initiateSession(Iterable<PacketExtension> sessionInitiateExtensions)
      throws OperationFailedException {
    sid = SessionIQ.generateSID();
    initiator = false;

    // Create the media description that we'd like to send to the other side.
    RtpDescriptionPacketExtension offer = getMediaHandler().createDescription();

    ProtocolProviderServiceJabberImpl protocolProvider = getProtocolProvider();

    sessionInitIQ =
        GTalkPacketFactory.createSessionInitiate(
            protocolProvider.getOurJID(), this.peerJID, sid, offer);

    if (sessionInitiateExtensions != null) {
      for (PacketExtension sessionInitiateExtension : sessionInitiateExtensions) {
        sessionInitIQ.addExtension(sessionInitiateExtension);
      }
    }

    protocolProvider.getConnection().sendPacket(sessionInitIQ);

    // for Google Voice JID without resource we do not harvest and send
    // candidates
    if (getAddress().endsWith(ProtocolProviderServiceJabberImpl.GOOGLE_VOICE_DOMAIN)) {
      return;
    }

    getMediaHandler()
        .harvestCandidates(
            offer.getPayloadTypes(),
            new CandidatesSender() {
              public void sendCandidates(Iterable<GTalkCandidatePacketExtension> candidates) {
                CallPeerGTalkImpl.this.sendCandidates(candidates);
              }
            });
  }