/**
   * {@inheritDoc}
   *
   * <p>Implements {@link RegistrationStateChangeListener}. Notifies this instance that there has
   * been a change in the <tt>RegistrationState</tt> of {@link #protocolProvider}. Subscribes this
   * instance to {@link ColibriConferenceIQ}s as soon as <tt>protocolProvider</tt> is registered and
   * unsubscribes it as soon as <tt>protocolProvider</tt> is unregistered.
   */
  public void registrationStateChanged(RegistrationStateChangeEvent ev) {
    RegistrationState registrationState = ev.getNewState();

    if (RegistrationState.REGISTERED.equals(registrationState)) {
      protocolProvider.getConnection().addPacketListener(this, this);
    } else if (RegistrationState.UNREGISTERED.equals(registrationState)) {
      XMPPConnection connection = protocolProvider.getConnection();

      if (connection != null) connection.removePacketListener(this);
    }
  }
  /**
   * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has been received.
   *
   * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been received
   */
  private void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ) {
    /*
     * The application is not a Jitsi Videobridge server, it is a client.
     * Consequently, the specified ColibriConferenceIQ is sent to it in
     * relation to the part of the application's functionality which makes
     * requests to a Jitsi Videobridge server i.e. CallJabberImpl.
     *
     * Additionally, the method processColibriConferenceIQ is presently tasked
     * with processing ColibriConferenceIQ requests only. They are SET IQs
     * sent by the Jitsi Videobridge server to notify the application about
     * updates in the states of (colibri) conferences organized by the
     * application.
     */
    if (IQ.Type.SET.equals(conferenceIQ.getType()) && conferenceIQ.getID() != null) {
      OperationSetBasicTelephony<?> basicTelephony =
          protocolProvider.getOperationSet(OperationSetBasicTelephony.class);

      if (basicTelephony != null) {
        Iterator<? extends Call> i = basicTelephony.getActiveCalls();

        while (i.hasNext()) {
          Call call = i.next();

          if (call instanceof CallJabberImpl) {
            CallJabberImpl callJabberImpl = (CallJabberImpl) call;
            MediaAwareCallConference conference = callJabberImpl.getConference();

            if ((conference != null) && conference.isJitsiVideobridge()) {
              /*
               * TODO We may want to disallow rogue CallJabberImpl
               * instances which may throw an exception to prevent
               * the conferenceIQ from reaching the CallJabberImpl
               * instance which it was meant for.
               */
              if (callJabberImpl.processColibriConferenceIQ(conferenceIQ)) break;
            }
          }
        }
      }
    }
  }
  /**
   * Implements {@link PacketListener}. Notifies this instance that a specific {@link Packet} (which
   * this instance has already expressed interest into by returning <tt>true</tt> from {@link
   * #accept(Packet)}) has been received.
   *
   * @param packet the <tt>Packet</tt> which has been received and which this instance is given a
   *     chance to process
   */
  public void processPacket(Packet packet) {
    /*
     * As we do elsewhere, acknowledge the receipt of the Packet first and
     * then go about our business with it.
     */
    IQ iq = (IQ) packet;

    if (iq.getType() == IQ.Type.SET)
      protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq));

    /*
     * Now that the acknowledging is out of the way, do go about our
     * business with the Packet.
     */
    ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq;
    boolean interrupted = false;

    try {
      processColibriConferenceIQ(conferenceIQ);
    } catch (Throwable t) {
      logger.error(
          "An error occurred during the processing of a " + packet.getClass().getName() + " packet",
          t);

      if (t instanceof InterruptedException) {
        /*
         * We cleared the interrupted state of the current Thread by
         * catching the InterruptedException. However, we do not really
         * care whether the current Thread has been interrupted - we
         * caught the InterruptedException because we want to swallow
         * any Throwable. Consequently, we should better restore the
         * interrupted state.
         */
        interrupted = true;
      } else if (t instanceof ThreadDeath) throw (ThreadDeath) t;
    }
    if (interrupted) Thread.currentThread().interrupt();
  }
 /**
  * Creates a conference call with the specified callees as call peers via a video bridge provided
  * by the parent Jabber provider.
  *
  * @param callees the list of addresses that we should call
  * @return the newly created conference call containing all CallPeers
  * @throws OperationFailedException if establishing the conference call fails
  * @throws OperationNotSupportedException if the provider does not have any conferencing features.
  */
 public Call createConfCall(String[] callees)
     throws OperationFailedException, OperationNotSupportedException {
   return protocolProvider
       .getOperationSet(OperationSetTelephonyConferencing.class)
       .createConfCall(callees, new MediaAwareCallConference(true));
 }
  /**
   * Indicates if there's an active video bridge available at this moment. The Jabber provider may
   * announce support for video bridge, but it should not be used for calling until it becomes
   * actually active.
   *
   * @return <tt>true</tt> to indicate that there's currently an active available video bridge,
   *     <tt>false</tt> - otherwise
   */
  public boolean isActive() {
    String jitsiVideobridge = protocolProvider.getJitsiVideobridge();

    return ((jitsiVideobridge != null) && (jitsiVideobridge.length() > 0));
  }
 /**
  * Invites the callee represented by the specified uri to an already existing call using a video
  * bridge provided by the parent Jabber provider. The difference between this method and
  * createConfCall is that inviteCalleeToCall allows a user to add new peers to an already
  * established conference.
  *
  * @param uri the callee to invite to an existing conf call.
  * @param call the call that we should invite the callee to.
  * @return the CallPeer object corresponding to the callee represented by the specified uri.
  * @throws OperationFailedException if inviting the specified callee to the specified call fails
  * @throws OperationNotSupportedException if allowing additional callees to a pre-established call
  *     is not supported.
  */
 public CallPeer inviteCalleeToCall(String uri, Call call)
     throws OperationFailedException, OperationNotSupportedException {
   return protocolProvider
       .getOperationSet(OperationSetTelephonyConferencing.class)
       .inviteCalleeToCall(uri, call);
 }