예제 #1
0
/**
 * Geoloc sharing service implementation
 *
 * @author Jean-Marc AUFFRET
 */
public class GeolocSharingServiceImpl extends IGeolocSharingService.Stub {
  /** List of service event listeners */
  private RemoteCallbackList<IJoynServiceRegistrationListener> serviceListeners =
      new RemoteCallbackList<IJoynServiceRegistrationListener>();

  /** List of geoloc sharing sessions */
  private static Hashtable<String, IGeolocSharing> gshSessions =
      new Hashtable<String, IGeolocSharing>();

  /** List of geoloc sharing invitation listeners */
  private RemoteCallbackList<INewGeolocSharingListener> listeners =
      new RemoteCallbackList<INewGeolocSharingListener>();

  /** The logger */
  private static Logger logger = Logger.getLogger(GeolocSharingServiceImpl.class.getName());

  /** Lock used for synchronization */
  private Object lock = new Object();

  /** Constructor */
  public GeolocSharingServiceImpl() {
    if (logger.isActivated()) {
      logger.info("Geoloc sharing service API is loaded");
    }
  }

  /** Close API */
  public void close() {
    // Clear list of sessions
    gshSessions.clear();

    if (logger.isActivated()) {
      logger.info("Geoloc sharing service API is closed");
    }
  }

  /**
   * Add an geoloc sharing session in the list
   *
   * @param session Geoloc sharing session
   */
  protected static void addGeolocSharingSession(GeolocSharingImpl session) {
    if (logger.isActivated()) {
      logger.debug("Add a geoloc sharing session in the list (size=" + gshSessions.size() + ")");
    }

    gshSessions.put(session.getSharingId(), session);
  }

  /**
   * Remove an geoloc sharing session from the list
   *
   * @param sessionId Session ID
   */
  protected static void removeGeolocSharingSession(String sessionId) {
    if (logger.isActivated()) {
      logger.debug(
          "Remove a geoloc sharing session from the list (size=" + gshSessions.size() + ")");
    }

    gshSessions.remove(sessionId);
  }

  /**
   * Returns true if the service is registered to the platform, else returns false
   *
   * @return Returns true if registered else returns false
   */
  public boolean isServiceRegistered() {
    return ServerApiUtils.isImsConnected();
  }

  /**
   * Registers a listener on service registration events
   *
   * @param listener Service registration listener
   */
  public void addServiceRegistrationListener(IJoynServiceRegistrationListener listener) {
    synchronized (lock) {
      if (logger.isActivated()) {
        logger.info("Add a service listener");
      }

      serviceListeners.register(listener);
    }
  }

  /**
   * Unregisters a listener on service registration events
   *
   * @param listener Service registration listener
   */
  public void removeServiceRegistrationListener(IJoynServiceRegistrationListener listener) {
    synchronized (lock) {
      if (logger.isActivated()) {
        logger.info("Remove a service listener");
      }

      serviceListeners.unregister(listener);
    }
  }

  /**
   * Receive registration event
   *
   * @param state Registration state
   */
  public void notifyRegistrationEvent(boolean state) {
    // Notify listeners
    synchronized (lock) {
      final int N = serviceListeners.beginBroadcast();
      for (int i = 0; i < N; i++) {
        try {
          if (state) {
            serviceListeners.getBroadcastItem(i).onServiceRegistered();
          } else {
            serviceListeners.getBroadcastItem(i).onServiceUnregistered();
          }
        } catch (Exception e) {
          if (logger.isActivated()) {
            logger.error("Can't notify listener", e);
          }
        }
      }
      serviceListeners.finishBroadcast();
    }
  }

  /**
   * Receive a new geoloc sharing invitation
   *
   * @param session Geoloc sharing session
   */
  public void receiveGeolocSharingInvitation(GeolocTransferSession session) {
    if (logger.isActivated()) {
      logger.info("Receive geoloc sharing invitation from " + session.getRemoteContact());
    }

    // Extract number from contact
    String number = PhoneUtils.extractNumberFromUri(session.getRemoteContact());

    // Add session in the list
    GeolocSharingImpl sessionApi = new GeolocSharingImpl(session);
    GeolocSharingServiceImpl.addGeolocSharingSession(sessionApi);

    // Broadcast intent related to the received invitation
    Intent intent = new Intent(GeolocSharingIntent.ACTION_NEW_INVITATION);
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
    intent.putExtra(GeolocSharingIntent.EXTRA_CONTACT, number);
    intent.putExtra(GeolocSharingIntent.EXTRA_DISPLAY_NAME, session.getRemoteDisplayName());
    intent.putExtra(GeolocSharingIntent.EXTRA_SHARING_ID, session.getSessionID());
    AndroidFactory.getApplicationContext().sendBroadcast(intent);

    // Notify geoloc sharing invitation listeners
    synchronized (lock) {
      final int N = listeners.beginBroadcast();
      for (int i = 0; i < N; i++) {
        try {
          listeners.getBroadcastItem(i).onNewGeolocSharing(session.getSessionID());
        } catch (Exception e) {
          if (logger.isActivated()) {
            logger.error("Can't notify listener", e);
          }
        }
      }
      listeners.finishBroadcast();
    }
  }

  /**
   * Shares a geolocation with a contact. An exception if thrown if there is no ongoing CS call. The
   * parameter contact supports the following formats: MSISDN in national or international format,
   * SIP address, SIP-URI or Tel-URI. If the format of the contact is not supported an exception is
   * thrown.
   *
   * @param contact Contact
   * @param geoloc Geolocation info
   * @param listener Geoloc sharing event listener
   * @return Geoloc sharing
   * @throws ServerApiException
   */
  public IGeolocSharing shareGeoloc(String contact, Geoloc geoloc, IGeolocSharingListener listener)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Initiate a geoloc sharing session with " + contact);
    }

    // Test IMS connection
    ServerApiUtils.testIms();

    try {
      // Create a geoloc content
      String msgId = ChatUtils.generateMessageId();
      GeolocPush geolocPush =
          new GeolocPush(
              geoloc.getLabel(),
              geoloc.getLatitude(),
              geoloc.getLongitude(),
              geoloc.getExpiration(),
              geoloc.getAccuracy());
      String geolocDoc =
          ChatUtils.buildGeolocDocument(
              geolocPush, ImsModule.IMS_USER_PROFILE.getPublicUri(), msgId);
      MmContent content =
          new GeolocContent("geoloc.xml", geolocDoc.getBytes().length, geolocDoc.getBytes());

      // Initiate a sharing session
      final GeolocTransferSession session =
          Core.getInstance()
              .getRichcallService()
              .initiateGeolocSharingSession(contact, content, geolocPush);

      // Add session listener
      GeolocSharingImpl sessionApi = new GeolocSharingImpl(session);
      sessionApi.addEventListener(listener);

      // Start the session
      Thread t =
          new Thread() {
            public void run() {
              session.startSession();
            }
          };
      t.start();

      // Add session in the list
      addGeolocSharingSession(sessionApi);
      return sessionApi;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Returns the list of geoloc sharings in progress
   *
   * @return List of geoloc sharings
   * @throws ServerApiException
   */
  public List<IBinder> getGeolocSharings() throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get geoloc sharing sessions");
    }

    try {
      ArrayList<IBinder> result = new ArrayList<IBinder>(gshSessions.size());
      for (Enumeration<IGeolocSharing> e = gshSessions.elements(); e.hasMoreElements(); ) {
        IGeolocSharing sessionApi = e.nextElement();
        result.add(sessionApi.asBinder());
      }
      return result;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Returns a current geoloc sharing from its unique ID
   *
   * @return Geoloc sharing
   * @throws ServerApiException
   */
  public IGeolocSharing getGeolocSharing(String sharingId) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get geoloc sharing session " + sharingId);
    }

    return gshSessions.get(sharingId);
  }

  /**
   * Registers a geoloc sharing invitation listener
   *
   * @param listener New geoloc sharing listener
   * @throws ServerApiException
   */
  public void addNewGeolocSharingListener(INewGeolocSharingListener listener)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Add a geoloc sharing invitation listener");
    }

    listeners.register(listener);
  }

  /**
   * Unregisters a geoloc sharing invitation listener
   *
   * @param listener New geoloc sharing listener
   * @throws ServerApiException
   */
  public void removeNewGeolocSharingListener(INewGeolocSharingListener listener)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Remove a geoloc sharing invitation listener");
    }

    listeners.unregister(listener);
  }

  /**
   * Returns service version
   *
   * @return Version
   * @see Build.VERSION_CODES
   * @throws ServerApiException
   */
  public int getServiceVersion() throws ServerApiException {
    return Build.API_VERSION;
  }
}
예제 #2
0
/**
 * Rich call service has in charge to monitor the GSM call in order to stop the current content
 * sharing when the call terminates, to process capability request from remote and to request remote
 * capabilities.
 *
 * @author jexa7410
 */
public class RichcallService extends ImsService {
  /** Video share features tags */
  public static final String[] FEATURE_TAGS_VIDEO_SHARE = {FeatureTags.FEATURE_3GPP_VIDEO_SHARE};

  /** Image share features tags */
  public static final String[] FEATURE_TAGS_IMAGE_SHARE = {
    FeatureTags.FEATURE_3GPP_VIDEO_SHARE, FeatureTags.FEATURE_3GPP_IMAGE_SHARE
  };

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param parent IMS module
   * @throws CoreException
   */
  public RichcallService(ImsModule parent) throws CoreException {
    super(parent, true);
  }

  /** Start the IMS service */
  public synchronized void start() {
    if (isServiceStarted()) {
      // Already started
      return;
    }
    setServiceStarted(true);
  }

  /** Stop the IMS service */
  public synchronized void stop() {
    if (!isServiceStarted()) {
      // Already stopped
      return;
    }
    setServiceStarted(false);
  }

  /** Check the IMS service */
  public void check() {}

  /**
   * Returns CSh sessions
   *
   * @return List of sessions
   */
  public Vector<ContentSharingSession> getCShSessions() {
    Vector<ContentSharingSession> result = new Vector<ContentSharingSession>();
    Enumeration<ImsServiceSession> list = getSessions();
    while (list.hasMoreElements()) {
      ImsServiceSession session = list.nextElement();
      result.add((ContentSharingSession) session);
    }
    return result;
  }

  /**
   * Initiate an image sharing session
   *
   * @param contact Remote contact
   * @param content Content to be shared
   * @return CSh session
   * @throws CoreException
   */
  public ImageTransferSession initiateImageSharingSession(String contact, MmContent content)
      throws CoreException {
    if (logger.isActivated()) {
      logger.info(
          "Initiate image sharing session with contact "
              + contact
              + ", file "
              + content.toString());
    }

    // Test if call is established
    if (!getImsModule().getCallManager().isCallConnected()) {
      if (logger.isActivated()) {
        logger.debug("Rich call not established: cancel the initiation");
      }
      throw new CoreException("Call not established");
    }

    // Reject if there are already 2 bidirectional sessions with a given contact
    boolean rejectInvitation = false;
    Vector<ContentSharingSession> currentSessions = getCShSessions();
    if (currentSessions.size() >= 2) {
      // Already a bidirectional session
      if (logger.isActivated()) {
        logger.debug("Max sessions reached");
      }
      rejectInvitation = true;
    } else if (currentSessions.size() == 1) {
      ContentSharingSession currentSession = currentSessions.elementAt(0);
      if (!(currentSession instanceof TerminatingImageTransferSession)) {
        // Originating session already used
        if (logger.isActivated()) {
          logger.debug("Max originating sessions reached");
        }
        rejectInvitation = true;
      } else if (!PhoneUtils.compareNumbers(contact, currentSession.getRemoteContact())) {
        // Not the same contact
        if (logger.isActivated()) {
          logger.debug("Only bidirectional session with same contact authorized");
        }
        rejectInvitation = true;
      }
    }
    if (rejectInvitation) {
      if (logger.isActivated()) {
        logger.debug("The max number of sharing sessions is achieved: cancel the initiation");
      }
      throw new CoreException("Max content sharing sessions achieved");
    }

    // Create a new session
    OriginatingImageTransferSession session =
        new OriginatingImageTransferSession(
            this, content, PhoneUtils.formatNumberToSipUri(contact));

    // Start the session
    session.startSession();
    return session;
  }

  /**
   * Initiate a pre-recorded video sharing session
   *
   * @param contact Remote contact
   * @param content Video content to share
   * @param player Media player
   * @return CSh session
   * @throws CoreException
   */
  public VideoStreamingSession initiatePreRecordedVideoSharingSession(
      String contact, VideoContent content, IMediaPlayer player) throws CoreException {
    if (logger.isActivated()) {
      logger.info(
          "Initiate a pre-recorded video sharing session with contact "
              + contact
              + ", file "
              + content.toString());
    }

    // Test if call is established
    if (!getImsModule().getCallManager().isCallConnected()) {
      if (logger.isActivated()) {
        logger.debug("Rich call not established: cancel the initiation");
      }
      throw new CoreException("Call not established");
    }

    // Reject if there are already 2 bidirectional sessions with a given contact
    boolean rejectInvitation = false;
    Vector<ContentSharingSession> currentSessions = getCShSessions();
    if (currentSessions.size() >= 2) {
      // Already a bidirectional session
      if (logger.isActivated()) {
        logger.debug("Max sessions reached");
      }
      rejectInvitation = true;
    } else if (currentSessions.size() == 1) {
      ContentSharingSession currentSession = currentSessions.elementAt(0);
      if (!(currentSession instanceof TerminatingVideoStreamingSession)) {
        // Originating session already used
        if (logger.isActivated()) {
          logger.debug("Max originating sessions reached");
        }
        rejectInvitation = true;
      } else if (!PhoneUtils.compareNumbers(contact, currentSession.getRemoteContact())) {
        // Not the same contact
        if (logger.isActivated()) {
          logger.debug("Only bidirectional session with same contact authorized");
        }
        rejectInvitation = true;
      }
    }
    if (rejectInvitation) {
      if (logger.isActivated()) {
        logger.debug("The max number of sharing sessions is achieved: cancel the initiation");
      }
      throw new CoreException("Max content sharing sessions achieved");
    }

    // Create a new session
    OriginatingPreRecordedVideoStreamingSession session =
        new OriginatingPreRecordedVideoStreamingSession(
            this, player, content, PhoneUtils.formatNumberToSipUri(contact));

    // Start the session
    session.startSession();
    return session;
  }

  /**
   * Initiate a live video sharing session
   *
   * @param contact Remote contact
   * @param content Video content to share
   * @param player Media player
   * @return CSh session
   * @throws CoreException
   * @throws RemoteException
   */
  public VideoStreamingSession initiateLiveVideoSharingSession(String contact, IMediaPlayer player)
      throws CoreException, RemoteException {
    if (logger.isActivated()) {
      logger.info("Initiate a live video sharing session");
    }

    // Test if call is established
    if (!getImsModule().getCallManager().isCallConnected()) {
      if (logger.isActivated()) {
        logger.debug("Rich call not established: cancel the initiation");
      }
      throw new CoreException("Call not established");
    }

    // Reject if there are already 2 bidirectional sessions with a given contact
    boolean rejectInvitation = false;
    Vector<ContentSharingSession> currentSessions = getCShSessions();
    if (currentSessions.size() >= 2) {
      // Already a bidirectional session
      if (logger.isActivated()) {
        logger.debug("Max sessions reached");
      }
      rejectInvitation = true;
    } else if (currentSessions.size() == 1) {
      ContentSharingSession currentSession = currentSessions.elementAt(0);
      if (!(currentSession instanceof TerminatingVideoStreamingSession)) {
        // Originating session already used
        if (logger.isActivated()) {
          logger.debug("Max originating sessions reached");
        }
        rejectInvitation = true;
      } else if (!PhoneUtils.compareNumbers(contact, currentSession.getRemoteContact())) {
        // Not the same contact
        if (logger.isActivated()) {
          logger.debug(
              "contact = "
                  + contact
                  + ", currentSession.getRemoteContact() = "
                  + currentSession.getRemoteContact());
          logger.debug("Only bidirectional session with same contact authorized");
        }
        rejectInvitation = true;
      }
    }
    if (rejectInvitation) {
      if (logger.isActivated()) {
        logger.debug("The max number of sharing sessions is achieved: cancel the initiation");
      }
      throw new CoreException("Max content sharing sessions achieved");
    }

    // Create a new session
    OriginatingLiveVideoStreamingSession session =
        new OriginatingLiveVideoStreamingSession(
            this,
            player,
            ContentManager.createGenericLiveVideoContent(),
            PhoneUtils.formatNumberToSipUri(contact));

    // Start the session
    session.startSession();
    return session;
  }

  /**
   * Receive a video sharing invitation
   *
   * @param invite Initial invite
   */
  public void receiveVideoSharingInvitation(SipRequest invite) {
    // Test if call is established
    if (!getImsModule().getCallManager().isCallConnected()) {
      if (logger.isActivated()) {
        logger.debug("Rich call not established: reject the invitation");
      }
      sendErrorResponse(invite, 606);
      return;
    }

    // Reject if there are already 2 bidirectional sessions with a given contact
    boolean rejectInvitation = false;
    String contact = SipUtils.getAssertedIdentity(invite);
    Vector<ContentSharingSession> currentSessions = getCShSessions();
    if (currentSessions.size() >= 2) {
      // Already a bidirectional session
      if (logger.isActivated()) {
        logger.debug("Max sessions reached");
      }
      rejectInvitation = true;
    } else if (currentSessions.size() == 1) {
      ContentSharingSession currentSession = currentSessions.elementAt(0);
      if (currentSession instanceof TerminatingVideoStreamingSession) {
        // Terminating session already used
        if (logger.isActivated()) {
          logger.debug("Max terminating sessions reached");
        }
        rejectInvitation = true;
      } else if (!PhoneUtils.compareNumbers(contact, currentSession.getRemoteContact())) {
        // Not the same contact
        if (logger.isActivated()) {
          logger.debug("Only bidirectional session with same contact authorized");
        }
        rejectInvitation = true;
      }
    }
    if (rejectInvitation) {
      if (logger.isActivated()) {
        logger.debug("The max number of sharing sessions is achieved: reject the invitation");
      }
      sendErrorResponse(invite, 486);
      return;
    }

    // Create a new session
    VideoStreamingSession session = new TerminatingVideoStreamingSession(this, invite);

    // Start the session
    session.startSession();
  }

  /**
   * Receive an image sharing invitation
   *
   * @param invite Initial invite
   */
  public void receiveImageSharingInvitation(SipRequest invite) {
    if (logger.isActivated()) {
      logger.info("Receive an image sharing session invitation");
    }

    // Test if call is established
    if (!getImsModule().getCallManager().isCallConnected()) {
      if (logger.isActivated()) {
        logger.debug("Rich call not established: reject the invitation");
      }
      sendErrorResponse(invite, 606);
      return;
    }

    // Reject if there are already 2 bidirectional sessions with a given contact
    boolean rejectInvitation = false;
    String contact = SipUtils.getAssertedIdentity(invite);
    Vector<ContentSharingSession> currentSessions = getCShSessions();
    if (currentSessions.size() >= 2) {
      // Already a bidirectional session
      if (logger.isActivated()) {
        logger.debug("Max sessions reached");
      }
      rejectInvitation = true;
    } else if (currentSessions.size() == 1) {
      ContentSharingSession currentSession = currentSessions.elementAt(0);
      if (currentSession instanceof TerminatingImageTransferSession) {
        // Terminating session already used
        if (logger.isActivated()) {
          logger.debug("Max terminating sessions reached");
        }
        rejectInvitation = true;
      } else if (!PhoneUtils.compareNumbers(contact, currentSession.getRemoteContact())) {
        // Not the same contact
        if (logger.isActivated()) {
          logger.debug("Only bidirectional session with same contact authorized");
        }
        rejectInvitation = true;
      }
    }
    if (rejectInvitation) {
      if (logger.isActivated()) {
        logger.debug("The max number of sharing sessions is achieved: reject the invitation");
      }
      sendErrorResponse(invite, 486);
      return;
    }

    // Create a new session
    ImageTransferSession session = new TerminatingImageTransferSession(this, invite);

    // Start the session
    session.startSession();

    // Notify listener
    getImsModule().getCore().getListener().handleContentSharingTransferInvitation(session);
  }

  /** M: Modified to fix rich call capability related issue.@{ */
  /**
   * Receive a capability request (options procedure)
   *
   * @param options Received options message
   */
  public void receiveCapabilityRequest(SipRequest options) {
    String contact = SipUtils.getAssertedIdentity(options);

    if (logger.isActivated()) {
      logger.debug("OPTIONS request received during a call from " + contact);
    }

    try {
      // Create 200 OK response
      String ipAddress =
          getImsModule().getCurrentNetworkInterface().getNetworkAccess().getIpAddress();
      boolean richcall = getImsModule().getCallManager().isRichcallSupportedWith(contact);
      SipResponse resp =
          SipMessageFactory.create200OkOptionsResponse(
              options,
              getImsModule().getSipManager().getSipStack().getLocalContact(),
              CapabilityUtils.getSupportedFeatureTags(richcall),
              CapabilityUtils.buildSdp(ipAddress, richcall));

      // Send 200 OK response
      getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 200 OK for OPTIONS", e);
      }
    }

    // Extract capabilities from the request
    Capabilities capabilities = CapabilityUtils.extractCapabilities(options);
    logger.debug("capabilities = " + capabilities);
    if (capabilities.isImSessionSupported()) {
      // The contact is RCS capable
      ContactsManager.getInstance()
          .setContactCapabilities(
              contact,
              capabilities,
              ContactInfo.RCS_CAPABLE,
              ContactInfo.REGISTRATION_STATUS_ONLINE);
      /** M: Added to fix the issue that RCS-e icon does not display in contact list of People.@{ */
      capabilities.setRcseContact(true);
      /** @} */
    } else {
      // The contact is not RCS
      ContactsManager.getInstance()
          .setContactCapabilities(
              contact, capabilities, ContactInfo.NOT_RCS, ContactInfo.REGISTRATION_STATUS_UNKNOWN);
      /** M: Added to fix the issue that RCS-e icon does not display in contact list of People.@{ */
      capabilities.setRcseContact(false);
      /** @} */
    }
    /** M: Added to fix the issue that RCS-e icon does not display in contact list of People.@{ */
    if (logger.isActivated()) {
      logger.debug(
          "receiveCapabilityRequest setRcseContact contact: "
              + contact
              + " "
              + capabilities.isImSessionSupported());
    }
    /** @} */
    // Notify listener
    getImsModule().getCore().getListener().handleCapabilitiesNotification(contact, capabilities);
  }
  /** @} */

  /** Abort all pending sessions */
  public void abortAllSessions() {
    if (logger.isActivated()) {
      logger.debug("Abort all pending sessions");
    }
    for (Enumeration<ImsServiceSession> e = getSessions(); e.hasMoreElements(); ) {
      ImsServiceSession session = (ImsServiceSession) e.nextElement();
      if (logger.isActivated()) {
        logger.debug("Abort pending session " + session.getSessionID());
      }
      session.abortSession();
    }
  }
}
/**
 * Rejoin a group chat session
 *
 * @author jexa7410
 */
public class RejoinGroupChatSession extends GroupChatSession {
  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param parent IMS service
   * @param rejoinId Rejoin ID
   * @param chatId Chat ID or contribution ID
   * @param subject Subject
   * @param participants List of participants
   */
  public RejoinGroupChatSession(
      ImsService parent,
      String rejoinId,
      String chatId,
      String subject,
      List<String> participants) {
    super(parent, rejoinId, new ListOfParticipant(participants));

    // Set subject
    if ((subject != null) && (subject.length() > 0)) {
      setSubject(subject);
    }

    // Create dialog path
    createOriginatingDialogPath();

    // Set contribution ID
    setContributionID(chatId);
  }

  /** Background processing */
  public void run() {
    try {
      if (logger.isActivated()) {
        logger.info("Rejoin an existing group chat session");
      }

      // Set setup mode
      String localSetup = createSetupOffer();
      if (logger.isActivated()) {
        logger.debug("Local setup attribute is " + localSetup);
      }

      // Set local port
      int localMsrpPort;
      if ("active".equals(localSetup)) {
        localMsrpPort = 9; // See RFC4145, Page 4
      } else {
        localMsrpPort = getMsrpMgr().getLocalMsrpPort();
      }

      // Build SDP part
      String ipAddress = getDialogPath().getSipStack().getLocalIpAddress();
      String sdp =
          SdpUtils.buildGroupChatSDP(
              ipAddress,
              localMsrpPort,
              getMsrpMgr().getLocalSocketProtocol(),
              getAcceptTypes(),
              getWrappedTypes(),
              localSetup,
              getMsrpMgr().getLocalMsrpPath(),
              SdpUtils.DIRECTION_SENDRECV);

      // Set the local SDP part in the dialog path
      getDialogPath().setLocalContent(sdp);

      // Create an INVITE request
      if (logger.isActivated()) {
        logger.info("Send INVITE");
      }
      SipRequest invite = createInviteRequest(sdp);

      // Set the Authorization header
      getAuthenticationAgent().setAuthorizationHeader(invite);

      // Set initial request in the dialog path
      getDialogPath().setInvite(invite);

      // Send INVITE request
      sendInvite(invite);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(new ChatError(ChatError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  /**
   * Create INVITE request
   *
   * @param content Content part
   * @return Request
   * @throws SipException
   */
  private SipRequest createInviteRequest(String content) throws SipException {
    SipRequest invite =
        SipMessageFactory.createInvite(
            getDialogPath(), getFeatureTags(), getAcceptContactTags(), content);

    // Test if there is a subject
    if (getSubject() != null) {
      // Add a subject header
      invite.addHeader(SubjectHeader.NAME, StringUtils.encodeUTF8(getSubject()));
    }

    // Add a contribution ID header
    invite.addHeader(ChatUtils.HEADER_CONTRIBUTION_ID, getContributionID());

    return invite;
  }

  /**
   * Create an INVITE request
   *
   * @return the INVITE request
   * @throws SipException
   */
  public SipRequest createInvite() throws SipException {
    return createInviteRequest(getDialogPath().getLocalContent());
  }

  /**
   * Handle 404 Session Not Found
   *
   * @param resp 404 response
   */
  public void handle404SessionNotFound(SipResponse resp) {
    // Rejoin session has failed, we update the database with status terminated by remote
    RichMessaging.getInstance().addChatSessionTerminationByRemote(this);

    // Notify listener
    handleError(new ChatError(ChatError.SESSION_NOT_FOUND, resp.getReasonPhrase()));
  }
}
/**
 * Rich call API service
 *
 * @author Jean-Marc AUFFRET
 */
public class VideoSharingServiceImpl extends IVideoSharingService.Stub {
  /** List of service event listeners */
  private RemoteCallbackList<IJoynServiceRegistrationListener> serviceListeners =
      new RemoteCallbackList<IJoynServiceRegistrationListener>();

  /** List of video sharing sessions */
  private static Hashtable<String, IVideoSharing> videoSharingSessions =
      new Hashtable<String, IVideoSharing>();

  /** List of video sharing invitation listeners */
  private RemoteCallbackList<INewVideoSharingListener> listeners =
      new RemoteCallbackList<INewVideoSharingListener>();

  /** Lock used for synchronization */
  private Object lock = new Object();

  /** The logger */
  private static Logger logger = Logger.getLogger(VideoSharingServiceImpl.class.getName());

  /** Constructor */
  public VideoSharingServiceImpl() {
    if (logger.isActivated()) {
      logger.info("Video sharing API is loaded");
    }
  }

  /** Close API */
  public void close() {
    // Clear list of sessions
    videoSharingSessions.clear();

    if (logger.isActivated()) {
      logger.info("Video sharing service API is closed");
    }
  }

  /**
   * Add a video sharing session in the list
   *
   * @param session Video sharing session
   */
  protected static void addVideoSharingSession(VideoSharingImpl session) {
    if (logger.isActivated()) {
      logger.debug(
          "Add a video sharing session in the list (size=" + videoSharingSessions.size() + ")");
    }

    videoSharingSessions.put(session.getSharingId(), session);
  }

  /**
   * Remove a video sharing session from the list
   *
   * @param sessionId Session ID
   */
  protected static void removeVideoSharingSession(String sessionId) {
    if (logger.isActivated()) {
      logger.debug(
          "Remove a video sharing session from the list (size="
              + videoSharingSessions.size()
              + ")");
    }

    videoSharingSessions.remove(sessionId);
  }

  /**
   * Returns true if the service is registered to the platform, else returns false
   *
   * @return Returns true if registered else returns false
   */
  public boolean isServiceRegistered() {
    return ServerApiUtils.isImsConnected();
  }

  /**
   * Registers a listener on service registration events
   *
   * @param listener Service registration listener
   */
  public void addServiceRegistrationListener(IJoynServiceRegistrationListener listener) {
    synchronized (lock) {
      if (logger.isActivated()) {
        logger.info("Add a service listener");
      }

      serviceListeners.register(listener);
    }
  }

  /**
   * Unregisters a listener on service registration events
   *
   * @param listener Service registration listener
   */
  public void removeServiceRegistrationListener(IJoynServiceRegistrationListener listener) {
    synchronized (lock) {
      if (logger.isActivated()) {
        logger.info("Remove a service listener");
      }

      serviceListeners.unregister(listener);
    }
  }

  /**
   * Receive registration event
   *
   * @param state Registration state
   */
  public void notifyRegistrationEvent(boolean state) {
    // Notify listeners
    synchronized (lock) {
      final int N = serviceListeners.beginBroadcast();
      for (int i = 0; i < N; i++) {
        try {
          if (state) {
            serviceListeners.getBroadcastItem(i).onServiceRegistered();
          } else {
            serviceListeners.getBroadcastItem(i).onServiceUnregistered();
          }
        } catch (Exception e) {
          if (logger.isActivated()) {
            logger.error("Can't notify listener", e);
          }
        }
      }
      serviceListeners.finishBroadcast();
    }
  }

  /**
   * Get the remote phone number involved in the current call
   *
   * @return Phone number or null if there is no call in progress
   * @throws ServerApiException
   */
  public String getRemotePhoneNumber() throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get remote phone number");
    }

    // Test core availability
    ServerApiUtils.testCore();

    try {
      return Core.getInstance().getImsModule().getCallManager().getRemoteParty();
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Receive a new video sharing invitation
   *
   * @param session Video sharing session
   */
  public void receiveVideoSharingInvitation(VideoStreamingSession session) {
    if (logger.isActivated()) {
      logger.info("Receive video sharing invitation from " + session.getRemoteContact());
    }

    // Extract number from contact
    String number = PhoneUtils.extractNumberFromUri(session.getRemoteContact());

    // Update rich call history
    VideoContent content = (VideoContent) session.getContent();
    RichCallHistory.getInstance()
        .addVideoSharing(
            number,
            session.getSessionID(),
            VideoSharing.Direction.INCOMING,
            content,
            VideoSharing.State.INVITED);

    // Add session in the list
    VideoSharingImpl sessionApi = new VideoSharingImpl(session);
    VideoSharingServiceImpl.addVideoSharingSession(sessionApi);

    // Broadcast intent related to the received invitation
    Intent intent = new Intent(VideoSharingIntent.ACTION_NEW_INVITATION);
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
    intent.putExtra(VideoSharingIntent.EXTRA_CONTACT, number);
    intent.putExtra(VideoSharingIntent.EXTRA_DISPLAY_NAME, session.getRemoteDisplayName());
    intent.putExtra(VideoSharingIntent.EXTRA_SHARING_ID, session.getSessionID());
    intent.putExtra(VideoSharingIntent.EXTRA_ENCODING, content.getEncoding());
    intent.putExtra(VideoSharingIntent.EXTRA_WIDTH, session.getVideoWidth());
    intent.putExtra(VideoSharingIntent.EXTRA_HEIGHT, session.getVideoHeight());
    AndroidFactory.getApplicationContext().sendBroadcast(intent);

    // Notify video sharing invitation listeners
    synchronized (lock) {
      final int N = listeners.beginBroadcast();
      for (int i = 0; i < N; i++) {
        try {
          listeners.getBroadcastItem(i).onNewVideoSharing(session.getSessionID());
        } catch (Exception e) {
          if (logger.isActivated()) {
            logger.error("Can't notify listener", e);
          }
        }
      }
      listeners.finishBroadcast();
    }
  }

  /**
   * Returns the configuration of video sharing service
   *
   * @return Configuration
   */
  public VideoSharingServiceConfiguration getConfiguration() {
    return new VideoSharingServiceConfiguration(
        RcsSettings.getInstance().getMaxVideoShareDuration());
  }

  /**
   * Shares a live video with a contact. The parameter renderer contains the video player provided
   * by the application. An exception if thrown if there is no ongoing CS call. The parameter
   * contact supports the following formats: MSISDN in national or international format, SIP
   * address, SIP-URI or Tel-URI. If the format of the contact is not supported an exception is
   * thrown.
   *
   * @param contact Contact
   * @param player Video player
   * @param listener Video sharing event listener
   * @return Video sharing
   * @throws ServerApiException
   */
  public IVideoSharing shareVideo(
      String contact, IVideoPlayer player, IVideoSharingListener listener)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Initiate a live video session with " + contact);
    }

    // Test IMS connection
    ServerApiUtils.testIms();

    // Test if at least the audio media is configured
    if (player == null) {
      throw new ServerApiException("Missing video player");
    }

    try {
      // Initiate a new session
      final VideoStreamingSession session =
          Core.getInstance().getRichcallService().initiateLiveVideoSharingSession(contact, player);

      // Update rich call history
      RichCallHistory.getInstance()
          .addVideoSharing(
              contact,
              session.getSessionID(),
              VideoSharing.Direction.OUTGOING,
              session.getContent(),
              VideoSharing.State.INITIATED);

      // Add session listener
      VideoSharingImpl sessionApi = new VideoSharingImpl(session);
      sessionApi.addEventListener(listener);

      // Start the session
      Thread t =
          new Thread() {
            public void run() {
              session.startSession();
            }
          };
      t.start();

      // Add session in the list
      addVideoSharingSession(sessionApi);
      return sessionApi;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Returns a current video sharing from its unique ID
   *
   * @return Video sharing or null if not found
   * @throws ServerApiException
   */
  public IVideoSharing getVideoSharing(String sharingId) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get video sharing session " + sharingId);
    }

    return videoSharingSessions.get(sharingId);
  }

  /**
   * Returns the list of video sharings in progress
   *
   * @return List of video sharings
   * @throws ServerApiException
   */
  public List<IBinder> getVideoSharings() throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get video sharing sessions");
    }

    try {
      ArrayList<IBinder> result = new ArrayList<IBinder>(videoSharingSessions.size());
      for (Enumeration<IVideoSharing> e = videoSharingSessions.elements(); e.hasMoreElements(); ) {
        IVideoSharing sessionApi = e.nextElement();
        result.add(sessionApi.asBinder());
      }
      return result;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Registers an video sharing invitation listener
   *
   * @param listener New video sharing listener
   * @throws ServerApiException
   */
  public void addNewVideoSharingListener(INewVideoSharingListener listener)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Add an video sharing invitation listener");
    }

    listeners.register(listener);
  }

  /**
   * Unregisters an video sharing invitation listener
   *
   * @param listener New video sharing listener
   * @throws ServerApiException
   */
  public void removeNewVideoSharingListener(INewVideoSharingListener listener)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Remove an video sharing invitation listener");
    }

    listeners.unregister(listener);
  }

  /**
   * Returns service version
   *
   * @return Version
   * @see JoynService.Build.VERSION_CODES
   * @throws ServerApiException
   */
  public int getServiceVersion() throws ServerApiException {
    return JoynService.Build.API_VERSION;
  }
}
/**
 * Originating file transfer HTTP session
 *
 * @author vfml3370
 */
public class OriginatingHttpGroupFileSharingSession extends HttpFileTransferSession
    implements HttpTransferEventListener {

  /** HTTP upload manager */
  private HttpUploadManager uploadManager;

  /** File information to send via chat */
  private String fileInfo = null;

  /** Chat session used to send file info */
  private ChatSession chatSession = null;

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param parent IMS service
   * @param content Content to be shared
   * @param conferenceId Conference ID
   * @param participants List of participants
   * @param thumbnail Thumbnail
   * @param chatSessionId Chat session ID
   * @param chatContributionId Chat contribution Id
   */
  public OriginatingHttpGroupFileSharingSession(
      ImsService parent,
      MmContent content,
      String conferenceId,
      ListOfParticipant participants,
      byte[] thumbnail,
      String chatSessionID,
      String chatContributionId) {
    super(parent, content, conferenceId, thumbnail, chatSessionID, chatContributionId);

    // Set participants involved in the transfer
    this.participants = participants;

    // Instantiate the upload manager
    uploadManager = new HttpUploadManager(getContent(), getThumbnail(), this);
  }

  /** Background processing */
  public void run() {
    try {
      if (logger.isActivated()) {
        logger.info("Initiate a new HTTP group file transfer session as originating");
      }

      // Upload the file to the HTTP server
      byte[] result = uploadManager.uploadFile();
      sendResultToContact(result);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("File transfer has failed", e);
      }

      // Unexpected error
      handleError(new FileSharingError(FileSharingError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  @Override
  public void interrupt() {
    super.interrupt();
    uploadManager.interrupt();
  }

  /** Send the file transfer information */
  private void sendFileTransferInfo() {
    // Send File transfer Info
    String mime = CpimMessage.MIME_TYPE;
    String from = ImsModule.IMS_USER_PROFILE.getPublicUri();
    String to = ChatUtils.ANOMYNOUS_URI;
    String msgId = IdGenerator.getIdentifier();

    // Send file info in CPIM message
    String content =
        ChatUtils.buildCpimMessageWithImdn(
            from, to, msgId, fileInfo, FileTransferHttpInfoDocument.MIME_TYPE);

    // Send content
    chatSession.sendDataChunks(ChatUtils.generateMessageId(), content, mime);
    RichMessaging.getInstance()
        .updateFileTransferChatId(getSessionID(), chatSession.getContributionID(), msgId);
  }

  /**
   * Prepare to send the info to terminating side
   *
   * @param result byte[] which contains the result of the 200 OK from the content server
   */
  private void sendResultToContact(byte[] result) {
    // Check if upload is cancelled
    if (uploadManager.isCancelled()) {
      return;
    }

    if (result != null && ChatUtils.parseFileTransferHttpDocument(result) != null) {
      fileInfo = new String(result);
      if (logger.isActivated()) {
        logger.debug("Upload done with success: " + fileInfo);
      }

      // Send the file transfer info via a chat message
      chatSession = (ChatSession) Core.getInstance().getImService().getSession(getChatSessionID());
      if (chatSession == null) {
        Vector<ChatSession> chatSessions =
            Core.getInstance().getImService().getImSessionsWith(participants.getList());
        try {
          chatSession = chatSessions.lastElement();
          setChatSessionID(chatSession.getSessionID());
          setContributionID(chatSession.getContributionID());
          if (logger.isActivated()) {
            logger.debug("Chat session found with participants");
          }
        } catch (NoSuchElementException nsee) {
          chatSession = null;
          if (logger.isActivated()) {
            logger.debug("No such Chat session found with participants");
          }
        }
      }
      if (chatSession != null) {
        // A chat session exists
        if (logger.isActivated()) {
          logger.debug("Send file transfer info via an existing chat session");
        }

        // Send file transfer info
        sendFileTransferInfo();

        // File transfered
        handleFileTransfered();
      } else {
        logger.debug("sendResultToContact error 1");
        // No chat error
        handleError(new FileSharingError(FileSharingError.NO_CHAT_SESSION));
      }
    } else {
      if (logger.isActivated()) {
        logger.debug("Upload has failed");
      }

      try {
        Thread.sleep(5 * 1000);
      } catch (InterruptedException e) {
        // Nothing to do
        logger.debug("sendResultToContact excepion in thread 1");
      }

      if (!uploadManager.isCancelled()) {
        logger.debug("Upload is cancelled123");
        try {
          Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
          // Nothing to do
          logger.debug("sendResultToContact excepion in thread 2");
        }
        if (!uploadManager.isCancelled()) {
          logger.debug("Upload is cancelled234");
          handleError(new FileSharingError(FileSharingError.MEDIA_UPLOAD_FAILED));
        }
      }

      // Upload error
    }
  }

  /** Pausing the transfer */
  @Override
  public void pauseFileTransfer() {
    fileTransferPaused();
    interruptSession();
    uploadManager.getListener().httpTransferPaused();
  }

  /** Resuming the transfer */
  @Override
  public void resumeFileTransfer() {
    fileTransferResumed();
    logger.debug("resumeFileTransfer1");
    new Thread(
            new Runnable() {
              public void run() {
                try {
                  byte[] result = uploadManager.resumeUpload();
                  logger.debug("resumeFileTransfer2");
                  sendResultToContact(result);
                } catch (Exception e) {
                  logger.debug("resumeFileTransfer exception");
                  // TODO Auto-generated catch block
                  e.printStackTrace();
                }
              }
            })
        .start();
  }
}
예제 #6
0
/**
 * RTP input stream
 *
 * @author jexa7410
 */
public class RtpInputStream implements ProcessorInputStream {
  /** Remote address */
  private String remoteAddress;

  /** Remote port */
  private int remotePort;

  /** Local port */
  private int localPort;

  /** RTP receiver */
  private RtpPacketReceiver rtpReceiver = null;

  /** RTCP receiver */
  private RtcpPacketReceiver rtcpReceiver = null;

  /** RTCP transmitter */
  private RtcpPacketTransmitter rtcpTransmitter = null;

  /** Input buffer */
  private Buffer buffer = new Buffer();

  /** Input format */
  private Format inputFormat = null;

  /** RTCP Session */
  private RtcpSession rtcpSession = null;

  /** The logger */
  private final Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param localPort Local port
   * @param inputFormat Input format
   */
  public RtpInputStream(String remoteAddress, int remotePort, int localPort, Format inputFormat) {
    this.remoteAddress = remoteAddress;
    this.remotePort = remotePort;
    this.localPort = localPort;
    this.inputFormat = inputFormat;

    rtcpSession = new RtcpSession(false, 16000);
  }

  /**
   * Open the input stream
   *
   * @throws Exception
   */
  public void open() throws Exception {
    // Create the RTP receiver
    rtpReceiver = new RtpPacketReceiver(localPort, rtcpSession);

    // Create the RTCP receiver
    rtcpReceiver = new RtcpPacketReceiver(localPort + 1, rtcpSession);
    rtcpReceiver.start();

    // Create the RTCP transmitter
    rtcpTransmitter =
        new RtcpPacketTransmitter(
            remoteAddress, remotePort + 1, rtcpSession, rtcpReceiver.getConnection());
    rtcpTransmitter.start();
  }

  /** Close the input stream */
  public void close() {
    try {
      // Close the RTCP transmitter
      if (rtcpTransmitter != null) rtcpTransmitter.close();

      // Close the RTP receiver
      if (rtpReceiver != null) {
        rtpReceiver.close();
      }

      // Close the RTCP receiver
      if (rtcpReceiver != null) {
        rtcpReceiver.close();
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't close correctly RTP ressources", e);
      }
    }
  }

  /**
   * Returns the RTP receiver
   *
   * @return RTP receiver
   */
  public RtpPacketReceiver getRtpReceiver() {
    return rtpReceiver;
  }

  /**
   * Returns the RTCP receiver
   *
   * @return RTCP receiver
   */
  public RtcpPacketReceiver getRtcpReceiver() {
    return rtcpReceiver;
  }

  /**
   * Read from the input stream without blocking
   *
   * @return Buffer
   * @throws Exception
   */
  public Buffer read() throws Exception {
    // Wait and read a RTP packet
    RtpPacket rtpPacket = rtpReceiver.readRtpPacket();
    if (rtpPacket == null) {
      return null;
    }

    // Create a buffer
    buffer.setData(rtpPacket.data);
    buffer.setLength(rtpPacket.payloadlength);
    buffer.setOffset(0);
    buffer.setFormat(inputFormat);
    buffer.setSequenceNumber(rtpPacket.seqnum);
    buffer.setRTPMarker(rtpPacket.marker != 0);
    buffer.setTimeStamp(rtpPacket.timestamp);
    /** M: Added for share progress control @{ */
    buffer.setExtensionHeader(rtpPacket.extensionHeader);
    /** @} */
    // Set inputFormat back to null
    inputFormat = null;
    return buffer;
  }
}
예제 #7
0
/**
 * Rich call API service
 *
 * @author jexa7410
 */
public class RichCallApiService extends IRichCallApi.Stub {
  /** List of image sharing sessions */
  private static Hashtable<String, IImageSharingSession> imageSharingSessions =
      new Hashtable<String, IImageSharingSession>();

  /** List of video sharing sessions */
  private static Hashtable<String, IVideoSharingSession> videoSharingSessions =
      new Hashtable<String, IVideoSharingSession>();

  /** List of geoloc sharing sessions */
  private static Hashtable<String, IGeolocSharingSession> geolocSharingSessions =
      new Hashtable<String, IGeolocSharingSession>();

  /** The logger */
  private static Logger logger = Logger.getLogger(RichCallApiService.class.getName());

  /** Constructor */
  public RichCallApiService() {
    if (logger.isActivated()) {
      logger.info("Rich call API is loaded");
    }
  }

  /** Close API */
  public void close() {
    // Clear lists of sessions
    imageSharingSessions.clear();
    videoSharingSessions.clear();
  }

  /**
   * Add an image sharing session in the list
   *
   * @param session Image sharing session
   */
  protected static void addImageSharingSession(ImageSharingSession session) {
    if (logger.isActivated()) {
      logger.debug(
          "Add an image sharing session in the list (size=" + imageSharingSessions.size() + ")");
    }
    imageSharingSessions.put(session.getSessionID(), session);
  }

  /**
   * Remove an image sharing session from the list
   *
   * @param sessionId Session ID
   */
  protected static void removeImageSharingSession(String sessionId) {
    if (logger.isActivated()) {
      logger.debug(
          "Remove an image sharing session from the list (size="
              + imageSharingSessions.size()
              + ")");
    }
    imageSharingSessions.remove(sessionId);
  }

  /**
   * Add a video sharing session in the list
   *
   * @param session Video sharing session
   */
  protected static void addVideoSharingSession(VideoSharingSession session) {
    if (logger.isActivated()) {
      logger.debug(
          "Add a video sharing session in the list (size=" + videoSharingSessions.size() + ")");
    }
    videoSharingSessions.put(session.getSessionID(), session);
  }

  /**
   * Remove a video sharing session from the list
   *
   * @param sessionId Session ID
   */
  protected static void removeVideoSharingSession(String sessionId) {
    if (logger.isActivated()) {
      logger.debug(
          "Remove a video sharing session from the list (size="
              + videoSharingSessions.size()
              + ")");
    }
    videoSharingSessions.remove(sessionId);
  }

  /**
   * Add a geoloc sharing session in the list
   *
   * @param session Geoloc sharing session
   */
  protected static void addGeolocSharingSession(GeolocSharingSession session) {
    if (logger.isActivated()) {
      logger.debug(
          "Add a geoloc sharing session in the list (size=" + geolocSharingSessions.size() + ")");
    }
    geolocSharingSessions.put(session.getSessionID(), session);
  }

  /**
   * Remove a geoloc sharing session from the list
   *
   * @param sessionId Session ID
   */
  protected static void removeGeolocSharingSession(String sessionId) {
    if (logger.isActivated()) {
      logger.debug(
          "Remove a geoloc sharing session from the list (size="
              + geolocSharingSessions.size()
              + ")");
    }
    geolocSharingSessions.remove(sessionId);
  }

  /**
   * Get the remote phone number involved in the current call
   *
   * @return Phone number or null if there is no call in progress
   * @throws ServerApiException
   */
  public String getRemotePhoneNumber() throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get remote phone number");
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    try {
      return Core.getInstance().getImsModule().getCallManager().getRemoteParty();
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Receive a new video sharing invitation
   *
   * @param session Video sharing session
   */
  public void receiveVideoSharingInvitation(VideoStreamingSession session) {
    if (logger.isActivated()) {
      logger.info("Receive video sharing invitation from " + session.getRemoteContact());
    }

    // Extract number from contact
    String number = PhoneUtils.extractNumberFromUri(session.getRemoteContact());
    VideoContent content = (VideoContent) session.getContent();

    // Update rich call history
    RichCall.getInstance()
        .addCall(
            number,
            session.getSessionID(),
            RichCallData.EVENT_INCOMING,
            content,
            RichCallData.STATUS_STARTED);

    // Add session in the list
    VideoSharingSession sessionApi = new VideoSharingSession(session);
    addVideoSharingSession(sessionApi);

    // Broadcast intent related to the received invitation
    Intent intent = new Intent(RichCallApiIntents.VIDEO_SHARING_INVITATION);
    intent.putExtra("contact", number);
    intent.putExtra("contactDisplayname", session.getRemoteDisplayName());
    intent.putExtra("sessionId", session.getSessionID());
    intent.putExtra("videotype", content.getEncoding());
    intent.putExtra("videowidth", content.getWidth());
    intent.putExtra("videoheight", content.getHeight());
    AndroidFactory.getApplicationContext().sendBroadcast(intent);
  }

  /**
   * Initiate a live video sharing session
   *
   * @param contact Contact
   * @param player Media player
   * @throws ServerApiException
   */
  public IVideoSharingSession initiateLiveVideoSharing(String contact, IVideoPlayer player)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Initiate a live video session with " + contact);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test IMS connection
    ServerApiUtils.testIms();

    try {
      // Initiate a new session
      VideoStreamingSession session =
          Core.getInstance().getRichcallService().initiateLiveVideoSharingSession(contact, player);

      // Update rich call history
      RichCall.getInstance()
          .addCall(
              contact,
              session.getSessionID(),
              RichCallData.EVENT_OUTGOING,
              session.getContent(),
              RichCallData.STATUS_STARTED);

      // Add session in the list
      VideoSharingSession sessionApi = new VideoSharingSession(session);
      addVideoSharingSession(sessionApi);
      return sessionApi;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Get current video sharing session from its session ID
   *
   * @param id Session ID
   * @return Session
   * @throws ServerApiException
   */
  public IVideoSharingSession getVideoSharingSession(String id) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get video sharing session " + id);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    // Return a session instance
    return videoSharingSessions.get(id);
  }

  /**
   * Get list of current video sharing sessions with a contact
   *
   * @param contact Contact
   * @return List of sessions
   * @throws ServerApiException
   */
  public List<IBinder> getVideoSharingSessionsWith(String contact) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get video sharing sessions with " + contact);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    try {
      Vector<ContentSharingSession> list =
          Core.getInstance().getRichcallService().getCShSessions(contact);
      ArrayList<IBinder> result = new ArrayList<IBinder>(list.size());
      for (int i = 0; i < list.size(); i++) {
        ContentSharingSession session = list.elementAt(i);
        IVideoSharingSession sessionApi = videoSharingSessions.get(session.getSessionID());
        if (sessionApi != null) {
          result.add(sessionApi.asBinder());
        }
      }
      return result;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Receive a new image sharing invitation
   *
   * @param session Image sharing session
   */
  public void receiveImageSharingInvitation(ImageTransferSession session) {
    if (logger.isActivated()) {
      logger.info("Receive image sharing invitation from " + session.getRemoteContact());
    }

    // Extract number from contact
    String number = PhoneUtils.extractNumberFromUri(session.getRemoteContact());

    // Update rich call history
    RichCall.getInstance()
        .addCall(
            number,
            session.getSessionID(),
            RichCallData.EVENT_INCOMING,
            session.getContent(),
            RichCallData.STATUS_STARTED);

    // Add session in the list
    ImageSharingSession sessionApi = new ImageSharingSession(session);
    addImageSharingSession(sessionApi);

    // Broadcast intent related to the received invitation
    Intent intent = new Intent(RichCallApiIntents.IMAGE_SHARING_INVITATION);
    intent.putExtra("contact", number);
    intent.putExtra("contactDisplayname", session.getRemoteDisplayName());
    intent.putExtra("sessionId", session.getSessionID());
    intent.putExtra("filename", session.getContent().getName());
    intent.putExtra("filesize", session.getContent().getSize());
    intent.putExtra("filetype", session.getContent().getEncoding());
    intent.putExtra("thumbnail", session.getThumbnail());
    AndroidFactory.getApplicationContext().sendBroadcast(intent);
  }

  /**
   * Initiate an image sharing session
   *
   * @param contact Contact
   * @param file Image file
   * @param thumbnail Thumbnail option
   * @throws ServerApiException
   */
  public IImageSharingSession initiateImageSharing(String contact, String file, boolean thumbnail)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Initiate an image sharing session with " + contact);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test IMS connection
    ServerApiUtils.testIms();

    try {
      // Create an image content
      FileDescription desc = FileFactory.getFactory().getFileDescription(file);
      MmContent content = ContentManager.createMmContentFromUrl(file, desc.getSize());

      // Initiate a sharing session
      ImageTransferSession session =
          Core.getInstance()
              .getRichcallService()
              .initiateImageSharingSession(contact, content, thumbnail);

      // Update rich call history
      RichCall.getInstance()
          .addCall(
              contact,
              session.getSessionID(),
              RichCallData.EVENT_OUTGOING,
              session.getContent(),
              RichCallData.STATUS_STARTED);

      // Add session in the list
      ImageSharingSession sessionApi = new ImageSharingSession(session);
      addImageSharingSession(sessionApi);
      return sessionApi;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Get current image sharing session from its session ID
   *
   * @param id Session ID
   * @return Session
   * @throws ServerApiException
   */
  public IImageSharingSession getImageSharingSession(String id) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get image sharing session " + id);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    // Return a session instance
    return imageSharingSessions.get(id);
  }

  /**
   * Get list of current image sharing sessions with a contact
   *
   * @param contact Contact
   * @return List of sessions
   * @throws ServerApiException
   */
  public List<IBinder> getImageSharingSessionsWith(String contact) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get image sharing sessions with " + contact);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    try {
      Vector<ContentSharingSession> list =
          Core.getInstance().getRichcallService().getCShSessions(contact);
      ArrayList<IBinder> result = new ArrayList<IBinder>(list.size());
      for (int i = 0; i < list.size(); i++) {
        ContentSharingSession session = list.elementAt(i);
        IImageSharingSession sessionApi = imageSharingSessions.get(session.getSessionID());
        if (sessionApi != null) {
          result.add(sessionApi.asBinder());
        }
      }
      return result;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Initiate a geoloc sharing session
   *
   * @param contact Contact
   * @param geoloc Geoloc info
   * @return Geoloc sharing session
   * @throws ServerApiException
   */
  public IGeolocSharingSession initiateGeolocSharing(String contact, GeolocPush geoloc)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Initiate a geoloc sharing session with " + contact);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test IMS connection
    ServerApiUtils.testIms();

    try {
      // Create a geoloc content
      String msgId = ChatUtils.generateMessageId();
      String geolocDoc =
          ChatUtils.buildGeolocDocument(geoloc, ImsModule.IMS_USER_PROFILE.getPublicUri(), msgId);
      MmContent content =
          new GeolocContent("geoloc.xml", geolocDoc.getBytes().length, geolocDoc.getBytes());

      // Initiate a sharing session
      GeolocTransferSession session =
          Core.getInstance()
              .getRichcallService()
              .initiateGeolocSharingSession(contact, content, geoloc);

      // Update rich call
      RichCall.getInstance()
          .addCall(
              contact,
              session.getSessionID(),
              RichCallData.EVENT_OUTGOING,
              session.getContent(),
              RichCallData.STATUS_STARTED);

      // Update rich messaging history
      GeolocMessage geolocMsg = new GeolocMessage(null, contact, geoloc, false);
      RichMessaging.getInstance().addOutgoingGeoloc(geolocMsg);

      // Add session in the list
      GeolocSharingSession sessionApi = new GeolocSharingSession(session);
      addGeolocSharingSession(sessionApi);
      return sessionApi;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Receive a new geoloc sharing invitation
   *
   * @param session Geoloc sharing session
   */
  public void receiveGeolocSharingInvitation(GeolocTransferSession session) {
    if (logger.isActivated()) {
      logger.info("Receive geoloc sharing invitation from " + session.getRemoteContact());
    }

    // Extract number from contact
    String number = PhoneUtils.extractNumberFromUri(session.getRemoteContact());

    // Update rich call history
    RichCall.getInstance()
        .addCall(
            number,
            session.getSessionID(),
            RichCallData.EVENT_INCOMING,
            session.getContent(),
            RichCallData.STATUS_STARTED);

    // Add session in the list
    GeolocSharingSession sessionApi = new GeolocSharingSession(session);
    addGeolocSharingSession(sessionApi);

    // Broadcast intent related to the received invitation
    Intent intent = new Intent(RichCallApiIntents.GEOLOC_SHARING_INVITATION);
    intent.putExtra("contact", number);
    intent.putExtra("contactDisplayname", session.getRemoteDisplayName());
    intent.putExtra("sessionId", session.getSessionID());
    AndroidFactory.getApplicationContext().sendBroadcast(intent);
  }

  /**
   * Get vodafone account mapped with the specific number.
   *
   * @param number The number to be mapped to vodafone account.
   * @return The vodafone account.
   * @throws ServerApiException
   */
  public String getVfAccountViaNumber(String number) throws ServerApiException {
    ServerApiUtils.testPermission();
    ServerApiUtils.testCore();
    return PhoneUtils.getVfAccountViaNumber(number);
  }

  /**
   * Get vodafone account mapped with the specific number.
   *
   * @param number The number to be mapped to vodafone account.
   * @return The vodafone account.
   * @throws ServerApiException
   */
  public String getNumberViaVfAccount(String number) throws ServerApiException {
    ServerApiUtils.testPermission();
    ServerApiUtils.testCore();
    return PhoneUtils.getNumberViaVfAccount(number);
  }

  /**
   * Get current geoloc sharing session from its session ID
   *
   * @param id Session ID
   * @return Session
   * @throws ServerApiException
   */
  public IGeolocSharingSession getGeolocSharingSession(String id) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get geoloc sharing session " + id);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    // Return a session instance
    return geolocSharingSessions.get(id);
  }

  /**
   * Set multiparty call
   *
   * @param state State
   * @throws ServerApiException
   */
  public void setMultiPartyCall(boolean state) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Set multiparty call to " + state);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    // Update call manager
    Core.getInstance().getImsModule().getCallManager().setMultiPartyCall(state);
  }

  /**
   * Set call hold
   *
   * @param state State
   * @throws ServerApiException
   */
  public void setCallHold(boolean state) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Set call hold to " + state);
    }

    // Check permission
    ServerApiUtils.testPermission();

    // Test core availability
    ServerApiUtils.testCore();

    // Update call manager
    Core.getInstance().getImsModule().getCallManager().setCallHold(state);
  }
}
예제 #8
0
/**
 * Abstract Group chat session
 *
 * @author jexa7410
 */
public abstract class GroupChatSession extends ChatSession {
  /** Conference event subscribe manager */
  private ConferenceEventSubscribeManager conferenceSubscriber =
      new ConferenceEventSubscribeManager(this);

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor for originating side
   *
   * @param parent IMS service
   * @param conferenceId Conference id
   * @param participants List of invited participants
   */
  public GroupChatSession(ImsService parent, String conferenceId, ListOfParticipant participants) {
    super(parent, conferenceId, participants);
  }

  /**
   * Constructor for terminating side
   *
   * @param parent IMS service
   * @param contact Remote contact
   */
  public GroupChatSession(ImsService parent, String contact) {
    super(parent, contact);
  }

  /**
   * Is group chat
   *
   * @return Boolean
   */
  public boolean isGroupChat() {
    return true;
  }

  /**
   * Returns the list of participants currently connected to the session
   *
   * @return List of participants
   */
  public ListOfParticipant getConnectedParticipants() {
    return conferenceSubscriber.getParticipants();
  }

  /**
   * Get replaced session ID
   *
   * @return Session ID
   */
  public String getReplacedSessionId() {
    String result = null;
    ExtensionHeader sessionReplace =
        (ExtensionHeader) getDialogPath().getInvite().getHeader(SipUtils.HEADER_SESSION_REPLACES);
    if (sessionReplace != null) {
      result = sessionReplace.getValue();
    } else {
      String content = getDialogPath().getRemoteContent();
      if (content != null) {
        int index1 = content.indexOf("Session-Replaces=");
        if (index1 != -1) {
          int index2 = content.indexOf("\"", index1);
          result = content.substring(index1 + 17, index2);
        }
      }
    }
    return result;
  }

  /**
   * Returns the conference event subscriber
   *
   * @return Subscribe manager
   */
  public ConferenceEventSubscribeManager getConferenceEventSubscriber() {
    return conferenceSubscriber;
  }

  /** Close media session */
  public void closeMediaSession() {
    // Stop the activity manager
    getActivityManager().stop();

    // Stop conference subscription
    conferenceSubscriber.terminate();

    // Close MSRP session
    closeMsrpSession();
  }

  /** Terminate session */
  public void terminateSession() {
    // Stop conference subscription
    conferenceSubscriber.terminate();

    // Terminate session
    super.terminateSession();
  }

  /**
   * Send a text message
   *
   * @param msgId Message-ID
   * @param txt Text message
   */
  public void sendTextMessage(String msgId, String txt) {
    // Send message in CPIM
    String from = ImsModule.IMS_USER_PROFILE.getPublicUri();
    String to = getRemoteContact();
    String content =
        ChatUtils.buildCpimMessage(from, to, StringUtils.encodeUTF8(txt), InstantMessage.MIME_TYPE);

    // Send data
    boolean result = sendDataChunks(msgId, content, CpimMessage.MIME_TYPE);

    // Update rich messaging history
    InstantMessage msg = new InstantMessage(msgId, getRemoteContact(), txt, false, new Date());
    RichMessaging.getInstance().addOutgoingChatMessage(msg, this);

    // Check if message has been sent with success or not
    if (!result) {
      // Update rich messaging history
      RichMessaging.getInstance().markChatMessageFailed(msgId);

      // Notify listeners
      for (int i = 0; i < getListeners().size(); i++) {
        /** M: add server date for delivery status @{ */
        ((ChatSessionListener) getListeners().get(i))
            .handleMessageDeliveryStatus(msgId, ImdnDocument.DELIVERY_STATUS_FAILED, -1L);
        /** @} */
      }
    }
  }

  /**
   * Send is composing status
   *
   * @param status Status
   */
  public void sendIsComposingStatus(boolean status) {
    String from = ImsModule.IMS_USER_PROFILE.getPublicUri();
    String to = getRemoteContact();
    String content =
        ChatUtils.buildCpimMessage(
            from, to, IsComposingInfo.buildIsComposingInfo(status), IsComposingInfo.MIME_TYPE);
    String msgId = ChatUtils.generateMessageId();
    sendDataChunks(msgId, content, CpimMessage.MIME_TYPE);
  }

  /**
   * Add a participant to the session
   *
   * @param participant Participant
   */
  public void addParticipant(String participant) {
    try {
      if (logger.isActivated()) {
        logger.debug("Add one participant (" + participant + ") to the session");
      }

      // Re-use INVITE dialog path
      SessionAuthenticationAgent authenticationAgent = getAuthenticationAgent();

      // Increment the Cseq number of the dialog path
      getDialogPath().incrementCseq();

      // Send REFER request
      if (logger.isActivated()) {
        logger.debug("Send REFER");
      }
      String contactUri = PhoneUtils.formatNumberToSipUri(participant);
      SipRequest refer = SipMessageFactory.createRefer(getDialogPath(), contactUri);
      SipTransactionContext ctx =
          getImsService()
              .getImsModule()
              .getSipManager()
              .sendSubsequentRequest(getDialogPath(), refer);

      // Wait response
      if (logger.isActivated()) {
        logger.debug("Wait response");
      }
      ctx.waitResponse(SipManager.TIMEOUT);

      // Analyze received message
      if (ctx.getStatusCode() == 407) {
        // 407 response received
        if (logger.isActivated()) {
          logger.debug("407 response received");
        }

        // Set the Proxy-Authorization header
        authenticationAgent.readProxyAuthenticateHeader(ctx.getSipResponse());

        // Increment the Cseq number of the dialog path
        getDialogPath().incrementCseq();

        // Create a second REFER request with the right token
        if (logger.isActivated()) {
          logger.info("Send second REFER");
        }
        refer = SipMessageFactory.createRefer(getDialogPath(), contactUri);

        // Set the Authorization header
        authenticationAgent.setProxyAuthorizationHeader(refer);

        // Send REFER request
        ctx =
            getImsService()
                .getImsModule()
                .getSipManager()
                .sendSubsequentRequest(getDialogPath(), refer);

        // Wait response
        if (logger.isActivated()) {
          logger.debug("Wait response");
        }
        ctx.waitResponse(SipManager.TIMEOUT);

        // Analyze received message
        if ((ctx.getStatusCode() >= 200) && (ctx.getStatusCode() < 300)) {
          // 200 OK response
          if (logger.isActivated()) {
            logger.debug("200 OK response received");
          }

          // Notify listeners
          for (int i = 0; i < getListeners().size(); i++) {
            ((ChatSessionListener) getListeners().get(i)).handleAddParticipantSuccessful();
          }
        } else {
          // Error
          if (logger.isActivated()) {
            logger.debug("REFER has failed (" + ctx.getStatusCode() + ")");
          }

          // Notify listeners
          for (int i = 0; i < getListeners().size(); i++) {
            ((ChatSessionListener) getListeners().get(i))
                .handleAddParticipantFailed(ctx.getReasonPhrase());
          }
        }
      } else if ((ctx.getStatusCode() >= 200) && (ctx.getStatusCode() < 300)) {
        // 200 OK received
        if (logger.isActivated()) {
          logger.debug("200 OK response received");
        }

        // Notify listeners
        for (int i = 0; i < getListeners().size(); i++) {
          ((ChatSessionListener) getListeners().get(i)).handleAddParticipantSuccessful();
        }
      } else {
        // Error responses
        if (logger.isActivated()) {
          logger.debug("No response received");
        }

        // Notify listeners
        for (int i = 0; i < getListeners().size(); i++) {
          ((ChatSessionListener) getListeners().get(i))
              .handleAddParticipantFailed(ctx.getReasonPhrase());
        }
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("REFER request has failed", e);
      }

      // Notify listeners
      for (int i = 0; i < getListeners().size(); i++) {
        ((ChatSessionListener) getListeners().get(i)).handleAddParticipantFailed(e.getMessage());
      }
    }
  }

  /**
   * Add a list of participants to the session
   *
   * @param participants List of participants
   */
  public void addParticipants(List<String> participants) {
    try {
      if (participants.size() == 1) {
        addParticipant(participants.get(0));
        return;
      }

      if (logger.isActivated()) {
        logger.debug("Add " + participants.size() + " participants to the session");
      }

      // Re-use INVITE dialog path
      SessionAuthenticationAgent authenticationAgent = getAuthenticationAgent();

      // Increment the Cseq number of the dialog path
      getDialogPath().incrementCseq();

      // Send REFER request
      if (logger.isActivated()) {
        logger.debug("Send REFER");
      }
      SipRequest refer = SipMessageFactory.createRefer(getDialogPath(), participants);
      SipTransactionContext ctx =
          getImsService().getImsModule().getSipManager().sendSipMessageAndWait(refer);

      // Wait response
      if (logger.isActivated()) {
        logger.debug("Wait response");
      }
      ctx.waitResponse(SipManager.TIMEOUT);

      // Analyze received message
      if (ctx.getStatusCode() == 407) {
        // 407 response received
        if (logger.isActivated()) {
          logger.debug("407 response received");
        }

        // Set the Proxy-Authorization header
        authenticationAgent.readProxyAuthenticateHeader(ctx.getSipResponse());

        // Increment the Cseq number of the dialog path
        getDialogPath().incrementCseq();

        // Create a second REFER request with the right token
        if (logger.isActivated()) {
          logger.info("Send second REFER");
        }
        refer = SipMessageFactory.createRefer(getDialogPath(), participants);

        // Set the Authorization header
        authenticationAgent.setProxyAuthorizationHeader(refer);

        // Send REFER request
        ctx = getImsService().getImsModule().getSipManager().sendSipMessageAndWait(refer);

        // Wait response
        if (logger.isActivated()) {
          logger.debug("Wait response");
        }
        ctx.waitResponse(SipManager.TIMEOUT);

        // Analyze received message
        if ((ctx.getStatusCode() >= 200) && (ctx.getStatusCode() < 300)) {
          // 200 OK response
          if (logger.isActivated()) {
            logger.debug("20x OK response received");
          }

          // Notify listeners
          for (int i = 0; i < getListeners().size(); i++) {
            ((ChatSessionListener) getListeners().get(i)).handleAddParticipantSuccessful();
          }
        } else {
          // Error
          if (logger.isActivated()) {
            logger.debug("REFER has failed (" + ctx.getStatusCode() + ")");
          }

          // Notify listeners
          for (int i = 0; i < getListeners().size(); i++) {
            ((ChatSessionListener) getListeners().get(i))
                .handleAddParticipantFailed(ctx.getReasonPhrase());
          }
        }
      } else if ((ctx.getStatusCode() >= 200) && (ctx.getStatusCode() < 300)) {
        // 200 OK received
        if (logger.isActivated()) {
          logger.debug("20x OK response received");
        }

        // Notify listeners
        for (int i = 0; i < getListeners().size(); i++) {
          ((ChatSessionListener) getListeners().get(i)).handleAddParticipantSuccessful();
        }
      } else {
        // Error responses
        if (logger.isActivated()) {
          logger.debug("No response received");
        }

        // Notify listeners
        for (int i = 0; i < getListeners().size(); i++) {
          ((ChatSessionListener) getListeners().get(i))
              .handleAddParticipantFailed(ctx.getReasonPhrase());
        }
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("REFER request has failed", e);
      }

      // Notify listeners
      for (int i = 0; i < getListeners().size(); i++) {
        ((ChatSessionListener) getListeners().get(i)).handleAddParticipantFailed(e.getMessage());
      }
    }
  }

  /**
   * Send message delivery status via MSRP
   *
   * @param contact Contact that requested the delivery status
   * @param msgId Message ID
   * @param status Status
   */
  public void sendMsrpMessageDeliveryStatus(String contact, String msgId, String status) {
    // NO IMDN for group chat
  }

  /** Reject the session invitation */
  public void rejectSession() {
    rejectSession(603);
  }
}
/**
 * SIP API service
 *
 * @author Jean-Marc AUFFRET
 */
public class MultimediaSessionServiceImpl extends IMultimediaSessionService.Stub {
  /** List of service event listeners */
  private RemoteCallbackList<IJoynServiceRegistrationListener> serviceListeners =
      new RemoteCallbackList<IJoynServiceRegistrationListener>();

  /** List of sessions */
  private static Hashtable<String, IMultimediaSession> sipSessions =
      new Hashtable<String, IMultimediaSession>();

  /** Lock used for synchronization */
  private Object lock = new Object();

  /** The logger */
  private static Logger logger = Logger.getLogger(MultimediaSessionServiceImpl.class.getName());

  /** Constructor */
  public MultimediaSessionServiceImpl() {
    if (logger.isActivated()) {
      logger.info("Multimedia session API is loaded");
    }
  }

  /** Close API */
  public void close() {
    // Clear list of sessions
    sipSessions.clear();

    if (logger.isActivated()) {
      logger.info("Multimedia session service API is closed");
    }
  }

  /**
   * Add a SIP session in the list
   *
   * @param session SIP session
   */
  protected static void addSipSession(MultimediaSessionImpl session) {
    if (logger.isActivated()) {
      logger.debug("Add a multimedia session in the list (size=" + sipSessions.size() + ")");
    }

    sipSessions.put(session.getSessionId(), session);
  }

  /**
   * Remove a SIP session from the list
   *
   * @param sessionId Session ID
   */
  protected static void removeSipSession(String sessionId) {
    if (logger.isActivated()) {
      logger.debug("Remove a multimedia session from the list (size=" + sipSessions.size() + ")");
    }

    sipSessions.remove(sessionId);
  }

  /**
   * Returns true if the service is registered to the platform, else returns false
   *
   * @return Returns true if registered else returns false
   */
  public boolean isServiceRegistered() {
    return ServerApiUtils.isImsConnected();
  }

  /**
   * Registers a listener on service registration events
   *
   * @param listener Service registration listener
   */
  public void addServiceRegistrationListener(IJoynServiceRegistrationListener listener) {
    synchronized (lock) {
      if (logger.isActivated()) {
        logger.info("Add a service listener");
      }

      serviceListeners.register(listener);
    }
  }

  /**
   * Unregisters a listener on service registration events
   *
   * @param listener Service registration listener
   */
  public void removeServiceRegistrationListener(IJoynServiceRegistrationListener listener) {
    synchronized (lock) {
      if (logger.isActivated()) {
        logger.info("Remove a service listener");
      }

      serviceListeners.unregister(listener);
    }
  }

  /**
   * Receive registration event
   *
   * @param state Registration state
   */
  public void notifyRegistrationEvent(boolean state) {
    // Notify listeners
    synchronized (lock) {
      final int N = serviceListeners.beginBroadcast();
      for (int i = 0; i < N; i++) {
        try {
          if (state) {
            serviceListeners.getBroadcastItem(i).onServiceRegistered();
          } else {
            serviceListeners.getBroadcastItem(i).onServiceUnregistered();
          }
        } catch (Exception e) {
          if (logger.isActivated()) {
            logger.error("Can't notify listener", e);
          }
        }
      }
      serviceListeners.finishBroadcast();
    }
  }

  /**
   * Receive a new SIP session invitation
   *
   * @param intent Resolved intent
   * @param session SIP session
   */
  public void receiveSipSessionInvitation(Intent intent, GenericSipSession session) {
    // Add session in the list
    MultimediaSessionImpl sessionApi = new MultimediaSessionImpl(session);
    MultimediaSessionServiceImpl.addSipSession(sessionApi);

    // Broadcast intent related to the received invitation
    String number = PhoneUtils.extractNumberFromUri(session.getRemoteContact());
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
    intent.putExtra(MultimediaSessionIntent.EXTRA_CONTACT, number);
    intent.putExtra(MultimediaSessionIntent.EXTRA_DISPLAY_NAME, session.getRemoteDisplayName());
    intent.putExtra(MultimediaSessionIntent.EXTRA_SESSION_ID, session.getSessionID());

    // Broadcast intent related to the received invitation
    AndroidFactory.getApplicationContext().sendBroadcast(intent);
  }

  /**
   * Initiates a new multimedia session for real time messaging with a remote contact and for a
   * given service. The messages exchanged in real time during the session may be from any type. The
   * parameter contact supports the following formats: MSISDN in national or international format,
   * SIP address, SIP-URI or Tel-URI. If the format of the contact is not supported an exception is
   * thrown.
   *
   * @param serviceId Service ID
   * @param contact Contact
   * @param listener Multimedia session event listener
   * @return Multimedia session
   * @throws ServerApiException
   */
  public IMultimediaSession initiateSession(
      String serviceId, String contact, IMultimediaSessionListener listener)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Initiate a multimedia session with " + contact);
    }

    // Test IMS connection
    ServerApiUtils.testIms();

    try {
      // Initiate a new session
      String featureTag =
          FeatureTags.FEATURE_RCSE
              + "=\""
              + FeatureTags.FEATURE_RCSE_EXTENSION
              + "."
              + serviceId
              + "\"";
      final GenericSipSession session =
          Core.getInstance().getSipService().initiateSession(contact, featureTag);

      // Add session listener
      MultimediaSessionImpl sessionApi = new MultimediaSessionImpl(session);
      sessionApi.addEventListener(listener);

      // Start the session
      Thread t =
          new Thread() {
            public void run() {
              session.startSession();
            }
          };
      t.start();

      // Add session in the list
      MultimediaSessionServiceImpl.addSipSession(sessionApi);
      return sessionApi;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Returns a current session from its unique session ID
   *
   * @return Multimedia session or null if not found
   * @throws ServerApiException
   */
  public IMultimediaSession getSession(String sessionId) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get multimedia session " + sessionId);
    }

    return sipSessions.get(sessionId);
  }

  /**
   * Returns the list of sessions associated to a given service ID
   *
   * @param serviceId Service ID
   * @return List of sessions
   * @throws ServerApiException
   */
  public List<IBinder> getSessions(String serviceId) throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Get multimedia sessions for service " + serviceId);
    }

    try {
      ArrayList<IBinder> result = new ArrayList<IBinder>();
      for (IMultimediaSession sessionApi : sipSessions.values()) {
        // Filter on the service ID
        if (sessionApi.getServiceId().equals(serviceId)) {
          result.add(sessionApi.asBinder());
        }
      }
      return result;
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Sends a message in pager mode to a contact and for a given service. The message may be any type
   * of content. The parameter contact supports the following formats: MSISDN in national or
   * international format, SIP address, SIP-URI or Tel-URI. If the format of the contact is not
   * supported an exception is thrown.
   *
   * @param serviceId Service ID
   * @param contact Contact
   * @param content Message content
   * @return Returns true if sent successfully else returns false
   * @throws ServerApiException
   */
  public boolean sendMessage(String serviceId, String contact, byte[] content)
      throws ServerApiException {
    if (logger.isActivated()) {
      logger.info("Send instant message to " + contact);
    }

    // Test IMS connection
    ServerApiUtils.testIms();

    try {
      // Send instant message
      String featureTag =
          FeatureTags.FEATURE_RCSE
              + "=\""
              + FeatureTags.FEATURE_RCSE_EXTENSION
              + "."
              + serviceId
              + "\"";
      return Core.getInstance().getSipService().sendInstantMessage(contact, featureTag, content);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Unexpected error", e);
      }
      throw new ServerApiException(e.getMessage());
    }
  }

  /**
   * Receive an instant message (SIP MESSAGE)
   *
   * @param intent Resolved intent
   * @param message Instant message request
   */
  public void receiveSipInstantMessage(Intent intent, SipRequest message) {
    // Broadcast intent related to the received invitation
    String contact = SipUtils.getAssertedIdentity(message);
    String number = PhoneUtils.extractNumberFromUri(contact);
    String displayName = SipUtils.getDisplayNameFromUri(message.getFrom());
    intent.putExtra(MultimediaMessageIntent.EXTRA_CONTACT, number);
    intent.putExtra(MultimediaMessageIntent.EXTRA_DISPLAY_NAME, displayName);
    intent.putExtra(MultimediaMessageIntent.EXTRA_CONTENT, message.getRawContent());

    // Broadcast intent related to the received invitation
    AndroidFactory.getApplicationContext().sendBroadcast(intent);
  }

  /**
   * Returns service version
   *
   * @return Version
   * @see JoynService.Build.VERSION_CODES
   * @throws ServerApiException
   */
  public int getServiceVersion() throws ServerApiException {
    return JoynService.Build.API_VERSION;
  }
}
예제 #10
0
/**
 * IMS service session
 *
 * @author jexa7410
 */
public abstract class ImsServiceSession extends Thread {
  /** Session invitation status */
  public static final int INVITATION_NOT_ANSWERED = 0;

  public static final int INVITATION_ACCEPTED = 1;
  public static final int INVITATION_REJECTED = 2;
  /** M: add for MSRPoTLS */
  protected static final String PROTOCOL_TLS = "TLS";

  protected static final String PROTOCOL_TCP = "TCP";

  /** M: Added to resolve the rich call 403 error.@{ */
  private static final int INIT_CSEQUENCE_NUMBER = 1;
  /** @} */

  /** IMS service */
  private ImsService imsService;

  /** Session ID */
  private String sessionId = SessionIdGenerator.getNewId();

  /** Remote contact */
  private String contact;

  /** Dialog path */
  private SipDialogPath dialogPath = null;

  /** Authentication agent */
  private SessionAuthenticationAgent authenticationAgent;

  /** Session invitation status */
  protected int invitationStatus = INVITATION_NOT_ANSWERED;

  /** Wait user answer for session invitation */
  protected Object waitUserAnswer = new Object();

  /** Session listeners */
  private Vector<ImsSessionListener> listeners = new Vector<ImsSessionListener>();

  /** Session timer manager */
  private SessionTimerManager sessionTimer = new SessionTimerManager(this);

  /** Ringing period (in seconds) */
  private int ringingPeriod = RcsSettings.getInstance().getRingingPeriod();

  /** Session interrupted flag */
  private boolean sessionInterrupted = false;

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param parent IMS service
   * @param contact Remote contact
   */
  public ImsServiceSession(ImsService imsService, String contact) {
    this.imsService = imsService;
    this.contact = contact;
    this.authenticationAgent = new SessionAuthenticationAgent(imsService.getImsModule());
  }

  /** Create originating dialog path */
  public void createOriginatingDialogPath() {
    // Set Call-Id
    String callId = getImsService().getImsModule().getSipManager().getSipStack().generateCallId();

    // Set the route path
    Vector<String> route =
        getImsService().getImsModule().getSipManager().getSipStack().getServiceRoutePath();

    // Create a dialog path
    dialogPath =
        new SipDialogPath(
            getImsService().getImsModule().getSipManager().getSipStack(),
            callId,
            /** M: Added to resolve the rich call 403 error.@{ */
            INIT_CSEQUENCE_NUMBER,
            /** @} */
            getRemoteContact(),
            ImsModule.IMS_USER_PROFILE.getPublicUri(),
            getRemoteContact(),
            route);

    // Set the authentication agent in the dialog path
    dialogPath.setAuthenticationAgent(getAuthenticationAgent());
  }

  /** M: Added to resolve the rich call 403 error.@{ */
  /** Create originating dialog path */
  public void createOriginatingDialogPath(String callId) {
    logger.debug("createOriginatingDialogPath(), callId = " + callId);
    // Set the route path
    Vector<String> route =
        getImsService().getImsModule().getSipManager().getSipStack().getServiceRoutePath();

    // Create a dialog path
    dialogPath =
        new SipDialogPath(
            getImsService().getImsModule().getSipManager().getSipStack(),
            callId,
            INIT_CSEQUENCE_NUMBER,
            getRemoteContact(),
            ImsModule.IMS_USER_PROFILE.getPublicUri(),
            getRemoteContact(),
            route);

    // Set the authentication agent in the dialog path
    dialogPath.setAuthenticationAgent(getAuthenticationAgent());
  }
  /** @} */

  /**
   * Create terminating dialog path
   *
   * @param invite Incoming invite
   */
  public void createTerminatingDialogPath(SipRequest invite) {
    // Set the call-id
    String callId = invite.getCallId();

    // Set target
    String target = invite.getContactURI();

    // Set local party
    String localParty = invite.getTo();

    // Set remote party
    String remoteParty = invite.getFrom();

    // Get the CSeq value
    long cseq = invite.getCSeq();

    // Set the route path with the Record-Route
    Vector<String> route = SipUtils.routeProcessing(invite, false);

    // Create a dialog path
    dialogPath =
        new SipDialogPath(
            getImsService().getImsModule().getSipManager().getSipStack(),
            callId,
            cseq,
            target,
            localParty,
            remoteParty,
            route);

    // Set the INVITE request
    dialogPath.setInvite(invite);

    // Set the remote tag
    dialogPath.setRemoteTag(invite.getFromTag());

    // Set the remote content part
    dialogPath.setRemoteContent(invite.getContent());

    // Set the session timer expire
    dialogPath.setSessionExpireTime(invite.getSessionTimerExpire());
  }

  /**
   * Add a listener for receiving events
   *
   * @param listener Listener
   */
  public void addListener(ImsSessionListener listener) {
    listeners.add(listener);
  }

  /** Remove a listener */
  public void removeListener(ImsSessionListener listener) {
    listeners.remove(listener);
  }

  /** Remove all listeners */
  public void removeListeners() {
    listeners.removeAllElements();
  }

  /**
   * Returns the event listeners
   *
   * @return Listeners
   */
  public Vector<ImsSessionListener> getListeners() {
    return listeners;
  }

  /**
   * Get the session timer manager
   *
   * @return Session timer manager
   */
  public SessionTimerManager getSessionTimerManager() {
    return sessionTimer;
  }

  /**
   * Is behind a NAT
   *
   * @return Boolean
   */
  public boolean isBehindNat() {
    return getImsService().getImsModule().getCurrentNetworkInterface().isBehindNat();
  }

  /** Start the session in background */
  public void startSession() {
    // Add the session in the session manager
    imsService.addSession(this);

    // Start the session
    start();
  }

  /**
   * Return the IMS service
   *
   * @return IMS service
   */
  public ImsService getImsService() {
    return imsService;
  }

  /**
   * Return the session ID
   *
   * @return Session ID
   */
  public String getSessionID() {
    return sessionId;
  }

  /**
   * Returns the remote contact
   *
   * @return String
   */
  public String getRemoteContact() {
    return contact;
  }

  /**
   * Returns display name of the remote contact
   *
   * @return String
   */
  public String getRemoteDisplayName() {
    String displayName = null;
    try {
      String from = getDialogPath().getInvite().getFrom();
      displayName = PhoneUtils.extractDisplayNameFromUri(from);
    } catch (Exception e) {
      displayName = null;
    }
    return displayName;
  }

  /**
   * Get the dialog path of the session
   *
   * @return Dialog path object
   */
  public SipDialogPath getDialogPath() {
    return dialogPath;
  }

  /**
   * Set the dialog path of the session
   *
   * @param dialog Dialog path
   */
  public void setDialogPath(SipDialogPath dialog) {
    dialogPath = dialog;
  }

  /**
   * Returns the authentication agent
   *
   * @return Authentication agent
   */
  public SessionAuthenticationAgent getAuthenticationAgent() {
    return authenticationAgent;
  }

  /**
   * Reject the session invitation
   *
   * @param code Error code
   */
  public void rejectSession(int code) {
    if (logger.isActivated()) {
      logger.debug("Session invitation has been rejected");
    }
    invitationStatus = INVITATION_REJECTED;

    // Unblock semaphore
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }

    // Decline the invitation
    sendErrorResponse(getDialogPath().getInvite(), getDialogPath().getLocalTag(), code);

    // Remove the session in the session manager
    imsService.removeSession(this);
  }

  /** Accept the session invitation */
  public void acceptSession() {
    if (logger.isActivated()) {
      logger.debug("Session invitation has been accepted");
    }
    invitationStatus = INVITATION_ACCEPTED;

    // Unblock semaphore
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }
  }

  /**
   * Wait session invitation answer
   *
   * @return Answer
   */
  public int waitInvitationAnswer() {
    if (invitationStatus != INVITATION_NOT_ANSWERED) {
      return invitationStatus;
    }

    if (logger.isActivated()) {
      logger.debug("Wait session invitation answer");
    }

    // Wait until received response or received timeout
    try {
      synchronized (waitUserAnswer) {
        waitUserAnswer.wait(ringingPeriod * 1000);
      }
    } catch (InterruptedException e) {
      sessionInterrupted = true;
    }

    return invitationStatus;
  }

  /** Interrupt session */
  public void interruptSession() {
    if (logger.isActivated()) {
      logger.debug("Interrupt the session");
    }

    try {
      // Unblock semaphore
      synchronized (waitUserAnswer) {
        waitUserAnswer.notifyAll();
      }

      if (!isSessionInterrupted()) {
        // Interrupt thread
        interrupt();
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't interrupt the session correctly", e);
      }
    }
    if (logger.isActivated()) {
      logger.debug("Session has been interrupted");
    }
  }

  /** M: do not send SIP BYE when timeout to distinguish BOOTED from DEPARTED @{ */
  /** Abort the session */
  public void abortSessionWithoutBye() {
    if (logger.isActivated()) {
      logger.info("abortSessionWithoutBye() entry");
    }

    // Interrupt the session
    interruptSession();

    // Terminate session
    terminateSessionWithoutBy();

    // Close media session
    closeMediaSession();

    // Remove the current session
    getImsService().removeSession(this);

    // Notify listeners
    int size = getListeners().size();
    for (int i = 0; i < size; i++) {
      getListeners().get(i).handleSessionAborted();
    }
  }

  /** @} */

  /** Abort the session */
  public void abortSession() {
    if (logger.isActivated()) {
      logger.info("Abort the session");
    }

    // Interrupt the session
    interruptSession();

    // Terminate session
    terminateSession();

    // Close media session
    closeMediaSession();

    // Remove the current session
    getImsService().removeSession(this);

    // Notify listeners
    for (int i = 0; i < getListeners().size(); i++) {
      getListeners().get(i).handleSessionAborted();
    }
  }

  /** M: do not send SIP BYE when timeout to distinguish BOOTED from DEPARTED @{ */
  /** Terminate session */
  public void terminateSessionWithoutBy() {
    if (dialogPath.isSessionTerminated()) {
      // Already terminated
      return;
    }

    // Stop session timer
    getSessionTimerManager().stop();

    // Update dialog path
    dialogPath.sessionTerminated();

    // Unblock semaphore (used for terminating side only)
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }
  }

  /** @} */

  /** Terminate session */
  public void terminateSession() {
    if (logger.isActivated()) {
      logger.debug("Terminate the session");
    }

    if (dialogPath.isSessionTerminated()) {
      // Already terminated
      return;
    }

    // Stop session timer
    getSessionTimerManager().stop();

    // Update dialog path
    dialogPath.sessionTerminated();

    // Unblock semaphore (used for terminating side only)
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }

    try {
      // Terminate the session
      if (dialogPath.isSigEstablished()) {
        // Increment the Cseq number of the dialog path
        getDialogPath().incrementCseq();

        // Send BYE without waiting a response
        getImsService().getImsModule().getSipManager().sendSipBye(getDialogPath());
      } else {
        // Send CANCEL without waiting a response
        getImsService().getImsModule().getSipManager().sendSipCancel(getDialogPath());
      }

      if (logger.isActivated()) {
        logger.debug("SIP session has been terminated");
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session termination has failed", e);
      }
    }
  }

  /**
   * Receive BYE request
   *
   * @param bye BYE request
   */
  public void receiveBye(SipRequest bye) {
    if (logger.isActivated()) {
      logger.info("Receive a BYE message from the remote");
    }

    // Close media session
    closeMediaSession();

    // Update the dialog path status
    getDialogPath().sessionTerminated();

    // Remove the current session
    getImsService().removeSession(this);

    // Stop session timer
    getSessionTimerManager().stop();

    // Notify listeners
    for (int i = 0; i < getListeners().size(); i++) {
      getListeners().get(i).handleSessionTerminatedByRemote();
    }
  }

  /**
   * Receive CANCEL request
   *
   * @param cancel CANCEL request
   */
  public void receiveCancel(SipRequest cancel) {
    if (logger.isActivated()) {
      logger.info("Receive a CANCEL message from the remote");
    }

    if (getDialogPath().isSigEstablished()) {
      if (logger.isActivated()) {
        logger.info(
            "Ignore the received CANCEL message from the remote (session already established)");
      }
      return;
    }

    // Close media session
    closeMediaSession();

    // Update dialog path
    getDialogPath().sessionCancelled();

    // Send a 487 Request terminated
    try {
      if (logger.isActivated()) {
        logger.info("Send 487 Request terminated");
      }
      SipResponse terminatedResp =
          SipMessageFactory.createResponse(
              getDialogPath().getInvite(), IdGenerator.getIdentifier(), 487);
      getImsService().getImsModule().getSipManager().sendSipResponse(terminatedResp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 487 error response", e);
      }
    }

    // Remove the current session
    getImsService().removeSession(this);

    // Notify listeners
    for (int i = 0; i < getListeners().size(); i++) {
      getListeners().get(i).handleSessionTerminatedByRemote();
    }

    // Request capabilities to the remote
    getImsService()
        .getImsModule()
        .getCapabilityService()
        .requestContactCapabilities(getDialogPath().getRemoteParty());
  }

  /**
   * Receive re-INVITE request
   *
   * @param reInvite re-INVITE request
   */
  public void receiveReInvite(SipRequest reInvite) {
    sessionTimer.receiveReInvite(reInvite);
  }

  /**
   * Receive UPDATE request
   *
   * @param update UPDATE request
   */
  public void receiveUpdate(SipRequest update) {
    sessionTimer.receiveUpdate(update);
  }

  /** Close media session */
  public abstract void closeMediaSession();

  /**
   * Send a 180 Ringing response to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   */
  public void send180Ringing(SipRequest request, String localTag) {
    try {
      SipResponse progress = SipMessageFactory.createResponse(request, localTag, 180);
      getImsService().getImsModule().getSipManager().sendSipResponse(progress);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send a 180 Ringing response");
      }
    }
  }

  /**
   * Send an error response to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   * @param code Response code
   */
  public void sendErrorResponse(SipRequest request, String localTag, int code) {
    try {
      // Send  error
      if (logger.isActivated()) {
        logger.info("Send " + code + " error response");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, localTag, code);
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send error response", e);
      }
    }
  }

  /**
   * Send a 603 "Decline" to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   */
  public void send603Decline(SipRequest request, String localTag) {
    try {
      // Send a 603 Decline error
      if (logger.isActivated()) {
        logger.info("Send 603 Decline");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, localTag, 603);
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 603 Decline response", e);
      }
    }
  }

  /**
   * Send a 486 "Busy" to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   */
  public void send486Busy(SipRequest request, String localTag) {
    try {
      // Send a 486 Busy error
      if (logger.isActivated()) {
        logger.info("Send 486 Busy");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, localTag, 486);
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 486 Busy response", e);
      }
    }
  }

  /**
   * Send a 415 "Unsupported Media Type" to the remote party
   *
   * @param request SIP request
   */
  public void send415Error(SipRequest request) {
    try {
      if (logger.isActivated()) {
        logger.info("Send 415 Unsupported Media Type");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, 415);
      // TODO: set Accept-Encoding header
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 415 error response", e);
      }
    }
  }

  /**
   * Create SDP setup offer (see RFC6135, RFC4145)
   *
   * @return Setup offer ("active" or "actpass")
   */
  public String createSetupOffer() {
    if (isBehindNat()) {
      // Active mode by default if there is a NAT. Active/passive mode is
      // exchanged in order to be compatible with UE not supporting COMEDIA
      return "actpass";
    } else {
      return "active";
    }
  }

  /**
   * Create SDP setup answer (see RFC6135, RFC4145)
   *
   * @param offer setup offer
   * @return Setup answer ("active" or "passive")
   */
  public String createSetupAnswer(String offer) {
    if (offer.equals("actpass")) {
      // Active mode by default if there is a NAT or AS IM
      return "active";
    } else if (offer.equals("active")) {
      // Passive mode
      return "passive";
    } else if (offer.equals("passive")) {
      // Active mode
      return "active";
    } else {
      // Passive mode by default
      return "passive";
    }
  }

  /**
   * Returns the response timeout (in seconds)
   *
   * @return Timeout
   */
  public int getResponseTimeout() {
    return ringingPeriod + SipManager.TIMEOUT;
  }

  /** M: Added to resolve the rich call 403 error.@{ */
  /**
   * Handle 403 error. First do re-register then send request again
   *
   * @param request The request was responded with 403
   */
  public void handle403Forbidden(SipRequest request) {
    if (logger.isActivated()) {
      logger.debug("handle403Forbidden() entry");
    }
    boolean isRegistered =
        imsService
            .getImsModule()
            .getCurrentNetworkInterface()
            .getRegistrationManager()
            .registration();
    if (logger.isActivated()) {
      logger.debug("re-register isRegistered: " + isRegistered);
    }
    if (isRegistered) {
      String callId = dialogPath.getCallId();
      SipRequest invite = createSipInvite(callId);
      if (invite != null) {
        try {
          sendInvite(invite);
        } catch (SipException e) {
          if (logger.isActivated()) {
            logger.debug("re send sip request failed.");
          }
          e.printStackTrace();
        }

      } else {
        if (logger.isActivated()) {
          logger.debug("handle403Forbidden() invite is null");
        }
      }
    }
    if (logger.isActivated()) {
      logger.debug("handle403Forbidden() exit");
    }
  }

  protected void sendInvite(SipRequest invite) throws SipException {
    logger.debug("ImsServiceSession::sendInvite(), do nothing in the parent class");
  }

  protected SipRequest createSipInvite(String callId) {
    logger.debug("ImsServiceSession::createSipInvite(), do nothing in the parent class");
    return null;
  }
  /** @} */

  /** M: add for MSRPoTLS @{ */
  protected String getCurrentProtocol() {
    if (logger.isActivated()) {
      logger.debug("getCurrentProtocol entry");
    }
    String protocol = getImsService().getImsModule().getCurrentNetworkInterface().getMsrpProtocol();
    if (logger.isActivated()) {
      logger.debug("getCurrentProtocol exit, protocol: " + protocol);
    }
    return protocol;
  }
  /** @} */

  /**
   * Is session interrupted
   *
   * @return Boolean
   */
  public boolean isSessionInterrupted() {
    return sessionInterrupted;
  }
}
/**
 * Originating file transfer session
 *
 * @author jexa7410
 */
public class OriginatingFileSharingSession extends FileSharingSession implements MsrpEventListener {
  /** MSRP manager */
  private MsrpManager msrpMgr = null;

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param parent IMS service
   * @param content Content to be shared
   * @param contact Remote contact
   */
  public OriginatingFileSharingSession(ImsService parent, MmContent content, String contact) {
    super(parent, content, contact);

    // Create dialog path
    createOriginatingDialogPath();
  }

  /** Background processing */
  public void run() {
    try {
      if (logger.isActivated()) {
        logger.info("Initiate a file transfer session as originating");
      }
      /** M: Modified to resolve the 403 error issue.@{ */
      SipRequest invite = createSipInvite();
      /** @} */

      // Send INVITE request
      sendInvite(invite);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(new FileSharingError(FileSharingError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }

    if (logger.isActivated()) {
      logger.debug("End of thread");
    }
  }

  /** M: Modified to resolve the 403 error issue.@{ */
  /** @return A sip invite request */
  @Override
  protected SipRequest createSipInvite(String callId) {
    logger.debug("createSipInvite(), callId = " + callId);
    createOriginatingDialogPath(callId);
    return createSipInvite();
  }

  private SipRequest createSipInvite() {
    logger.debug("createSipInvite()");
    // Set setup mode
    String localSetup = createSetupOffer();
    if (logger.isActivated()) {
      logger.debug("Local setup attribute is " + localSetup);
    }

    // Set local port
    int localMsrpPort = 9; // See RFC4145, Page 4

    // Create the MSRP manager
    String localIpAddress =
        getImsService()
            .getImsModule()
            .getCurrentNetworkInterface()
            .getNetworkAccess()
            .getIpAddress();
    msrpMgr = new MsrpManager(localIpAddress, localMsrpPort);

    // Build SDP part
    String ntpTime = SipUtils.constructNTPtime(System.currentTimeMillis());
    String ipAddress = getDialogPath().getSipStack().getLocalIpAddress();
    /** M: add for MSRPoTLS @{ */
    String protocol = getCurrentProtocol();
    String sdp = null;
    if (PROTOCOL_TLS.equals(protocol)) {
      sdp =
          "v=0"
              + SipUtils.CRLF
              + "o=- "
              + ntpTime
              + " "
              + ntpTime
              + " IN IP4 "
              + ipAddress
              + SipUtils.CRLF
              + "s=-"
              + SipUtils.CRLF
              + "c=IN IP4 "
              + ipAddress
              + SipUtils.CRLF
              + "t=0 0"
              + SipUtils.CRLF
              + "m=message "
              + localMsrpPort
              + " TCP/TLS/MSRP *"
              + SipUtils.CRLF
              + "a=path:"
              + msrpMgr.getLocalMsrpsPath()
              + SipUtils.CRLF
              + "a=fingerprint:"
              + KeyStoreManager.getFingerPrint()
              + SipUtils.CRLF
              + "a=setup:"
              + localSetup
              + SipUtils.CRLF
              + "a=accept-types: "
              + getContent().getEncoding()
              + SipUtils.CRLF
              + "a=file-transfer-id:"
              + getFileTransferId()
              + SipUtils.CRLF
              + "a=file-disposition:attachment"
              + SipUtils.CRLF
              + "a=sendonly"
              + SipUtils.CRLF;
    } else {
      sdp =
          "v=0"
              + SipUtils.CRLF
              + "o=- "
              + ntpTime
              + " "
              + ntpTime
              + " IN IP4 "
              + ipAddress
              + SipUtils.CRLF
              + "s=-"
              + SipUtils.CRLF
              + "c=IN IP4 "
              + ipAddress
              + SipUtils.CRLF
              + "t=0 0"
              + SipUtils.CRLF
              + "m=message "
              + localMsrpPort
              + " TCP/MSRP *"
              + SipUtils.CRLF
              + "a=path:"
              + msrpMgr.getLocalMsrpPath()
              + SipUtils.CRLF
              + "a=setup:"
              + localSetup
              + SipUtils.CRLF
              + "a=accept-types: "
              + getContent().getEncoding()
              + SipUtils.CRLF
              + "a=file-transfer-id:"
              + getFileTransferId()
              + SipUtils.CRLF
              + "a=file-disposition:attachment"
              + SipUtils.CRLF
              + "a=sendonly"
              + SipUtils.CRLF;
    }
    int maxSize = FileSharingSession.getMaxFileSharingSize();
    if (maxSize > 0) {
      sdp += "a=max-size:" + maxSize + SipUtils.CRLF;
    }

    /** @} */
    // Set File-selector attribute
    String selector = getFileSelectorAttribute();
    if (selector != null) {
      sdp += "a=file-selector:" + selector + SipUtils.CRLF;
    }

    // Set File-location attribute
    String location = getFileLocationAttribute();
    if (location != null) {
      sdp += "a=file-location:" + location + SipUtils.CRLF;
    }

    // Set the local SDP part in the dialog path
    getDialogPath().setLocalContent(sdp);

    // Create an INVITE request
    if (logger.isActivated()) {
      logger.info("Create INVITE");
    }
    try {
      SipRequest invite;
      invite =
          SipMessageFactory.createInvite(
              getDialogPath(), InstantMessagingService.FT_FEATURE_TAGS, sdp);
      // Set the Authorization header
      getAuthenticationAgent().setAuthorizationHeader(invite);

      // Set initial request in the dialog path
      getDialogPath().setInvite(invite);
      return invite;
    } catch (SipException e) {
      e.printStackTrace();
    } catch (CoreException e) {
      e.printStackTrace();
    }
    logger.error("Create sip invite failed, return null.");
    return null;
  }
  /** @} */

  /**
   * Send INVITE message
   *
   * @param invite SIP INVITE
   * @throws SipException
   */
  @Override
  /** M: Modified to resolve the 403 error issue.@{ */
  protected void sendInvite(SipRequest invite) throws SipException {
    /** @} */
    // Send INVITE request
    SipTransactionContext ctx =
        getImsService().getImsModule().getSipManager().sendSipMessageAndWait(invite);

    // Wait response
    ctx.waitResponse(getResponseTimeout());

    // Analyze the received response
    if (ctx.isSipResponse()) {
      // A response has been received
      if (ctx.getStatusCode() == 200) {
        // 200 OK
        handle200OK(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 407) {
        // 407 Proxy Authentication Required
        handle407Authentication(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 422) {
        // 422 Session Interval Too Small
        handle422SessionTooSmall(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 603) {
        // 603 Invitation declined
        handleError(
            new FileSharingError(
                FileSharingError.SESSION_INITIATION_DECLINED, ctx.getReasonPhrase()));
      } else if (ctx.getStatusCode() == 487) {
        // 487 Invitation cancelled
        handleError(
            new FileSharingError(
                FileSharingError.SESSION_INITIATION_CANCELLED, ctx.getReasonPhrase()));
      }
      /** M: Modified to resolve the 403 error issue.@{ */
      else if (ctx.getStatusCode() == 403) {
        handle403Forbidden(invite);
      }
      /** @} */
      else {
        // Other error response
        handleError(
            new FileSharingError(
                FileSharingError.SESSION_INITIATION_FAILED,
                ctx.getStatusCode() + " " + ctx.getReasonPhrase()));
      }
    } else {
      if (logger.isActivated()) {
        logger.debug("No response received for INVITE");
      }
      /**
       * M:ALPS00507513. ADDED to reslove issue of wrong prompt in case of file transfer timeout@{
       */
      // No response received: timeout
      handleError(new FileSharingError(FileSharingError.SESSION_INITIATION_TIMEOUT));
      /** @}* */
    }
  }

  /**
   * Handle 200 0K response
   *
   * @param resp 200 OK response
   */
  public void handle200OK(SipResponse resp) {
    try {
      // 200 OK received
      if (logger.isActivated()) {
        logger.info("200 OK response received");
      }

      // The signalisation is established
      getDialogPath().sigEstablished();

      // Set the remote tag
      getDialogPath().setRemoteTag(resp.getToTag());

      // Set the target
      getDialogPath().setTarget(resp.getContactURI());

      // Set the route path with the Record-Route header
      Vector<String> newRoute = SipUtils.routeProcessing(resp, true);
      getDialogPath().setRoute(newRoute);

      // Set the remote SDP part
      getDialogPath().setRemoteContent(resp.getContent());

      // Parse the remote SDP part
      SdpParser parser = new SdpParser(getDialogPath().getRemoteContent().getBytes());
      Vector<MediaDescription> media = parser.getMediaDescriptions();
      MediaDescription mediaDesc = media.elementAt(0);
      MediaAttribute attr = mediaDesc.getMediaAttribute("path");
      String remoteMsrpPath = attr.getValue();
      String remoteHost = SdpUtils.extractRemoteHost(parser.sessionDescription.connectionInfo);
      int remotePort = mediaDesc.port;

      // Send ACK request
      if (logger.isActivated()) {
        logger.info("Send ACK");
      }
      getImsService().getImsModule().getSipManager().sendSipAck(getDialogPath());

      // The session is established
      getDialogPath().sessionEstablished();

      // Create the MSRP client session
      MsrpSession session =
          msrpMgr.createMsrpClientSession(remoteHost, remotePort, remoteMsrpPath, this);
      session.setFailureReportOption(false);
      session.setSuccessReportOption(true);

      // Open the MSRP session
      msrpMgr.openMsrpSession();

      // Start session timer
      if (getSessionTimerManager().isSessionTimerActivated(resp)) {
        getSessionTimerManager()
            .start(resp.getSessionTimerRefresher(), resp.getSessionTimerExpire());
      }

      // Notify listeners
      for (int i = 0; i < getListeners().size(); i++) {
        getListeners().get(i).handleSessionStarted();
      }

      // Start sending data chunks
      byte[] data = getContent().getData();
      InputStream stream;
      if (data == null) {
        // Load data from URL
        stream = FileFactory.getFactory().openFileInputStream(getContent().getUrl());
      } else {
        // Load data from memory
        stream = new ByteArrayInputStream(data);
      }
      msrpMgr.sendChunks(
          stream,
          ChatUtils.generateMessageId(),
          getContent().getEncoding(),
          getContent().getSize());
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(new FileSharingError(FileSharingError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  /**
   * Handle 407 Proxy Authentication Required
   *
   * @param resp 407 response
   */
  public void handle407Authentication(SipResponse resp) {
    try {
      if (logger.isActivated()) {
        logger.info("407 response received");
      }

      // Set the remote tag
      getDialogPath().setRemoteTag(resp.getToTag());

      // Update the authentication agent
      getAuthenticationAgent().readProxyAuthenticateHeader(resp);

      // Increment the Cseq number of the dialog path
      getDialogPath().incrementCseq();

      // Create a second INVITE request with the right token
      if (logger.isActivated()) {
        logger.info("Send second INVITE");
      }
      SipRequest invite =
          SipMessageFactory.createInvite(
              getDialogPath(),
              InstantMessagingService.FT_FEATURE_TAGS,
              getDialogPath().getLocalContent());

      // Reset initial request in the dialog path
      getDialogPath().setInvite(invite);

      // Set the Proxy-Authorization header
      getAuthenticationAgent().setProxyAuthorizationHeader(invite);

      // Send INVITE request
      sendInvite(invite);

    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(new FileSharingError(FileSharingError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  /**
   * Handle error
   *
   * @param error Error
   */
  public void handleError(FileSharingError error) {
    if (isInterrupted()) {
      return;
    }

    // Error
    if (logger.isActivated()) {
      logger.info("Session error: " + error.getErrorCode() + ", reason=" + error.getMessage());
    }

    // Close media session
    closeMediaSession();

    // Remove the current session
    getImsService().removeSession(this);

    // Notify listeners
    for (int j = 0; j < getListeners().size(); j++) {
      ((FileSharingSessionListener) getListeners().get(j)).handleTransferError(error);
    }
  }

  /**
   * Handle 422 response
   *
   * @param resp 422 response
   */
  private void handle422SessionTooSmall(SipResponse resp) {
    try {
      // 422 response received
      if (logger.isActivated()) {
        logger.info("422 response received");
      }

      // Extract the Min-SE value
      int minExpire = SipUtils.getMinSessionExpirePeriod(resp);
      if (minExpire == -1) {
        if (logger.isActivated()) {
          logger.error("Can't read the Min-SE value");
        }
        handleError(
            new FileSharingError(FileSharingError.UNEXPECTED_EXCEPTION, "No Min-SE value found"));
        return;
      }

      // Set the min expire value
      getDialogPath().setMinSessionExpireTime(minExpire);

      // Set the expire value
      getDialogPath().setSessionExpireTime(minExpire);

      // Increment the Cseq number of the dialog path
      getDialogPath().incrementCseq();

      // Create a new INVITE with the right expire period
      if (logger.isActivated()) {
        logger.info("Send new INVITE");
      }
      SipRequest invite =
          SipMessageFactory.createInvite(
              getDialogPath(),
              InstantMessagingService.FT_FEATURE_TAGS,
              getDialogPath().getLocalContent());

      // Set the Authorization header
      getAuthenticationAgent().setAuthorizationHeader(invite);

      // Reset initial request in the dialog path
      getDialogPath().setInvite(invite);

      // Send INVITE request
      sendInvite(invite);

    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(new FileSharingError(FileSharingError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  /**
   * Data has been transfered
   *
   * @param msgId Message ID
   */
  public void msrpDataTransfered(String msgId) {
    if (logger.isActivated()) {
      logger.info("Data transfered");
    }

    // File has been transfered
    fileTransfered();

    // Close the media session
    closeMediaSession();

    // Terminate session
    terminateSession();

    // Remove the current session
    getImsService().removeSession(this);

    // Notify listeners
    for (int j = 0; j < getListeners().size(); j++) {
      ((FileSharingSessionListener) getListeners().get(j))
          .handleFileTransfered(getContent().getUrl());
    }
  }

  /**
   * Data transfer has been received
   *
   * @param msgId Message ID
   * @param data Received data
   * @param mimeType Data mime-type
   */
  public void msrpDataReceived(String msgId, byte[] data, String mimeType) {
    // Not used in originating side
  }

  /**
   * Data transfer in progress
   *
   * @param currentSize Current transfered size in bytes
   * @param totalSize Total size in bytes
   */
  public void msrpTransferProgress(long currentSize, long totalSize) {
    // Notify listeners
    for (int j = 0; j < getListeners().size(); j++) {
      ((FileSharingSessionListener) getListeners().get(j))
          .handleTransferProgress(currentSize, totalSize);
    }
  }

  /** Data transfer has been aborted */
  public void msrpTransferAborted() {
    if (logger.isActivated()) {
      logger.info("Data transfer aborted");
    }
  }

  /**
   * Data transfer error
   *
   * @param error Error
   */
  public void msrpTransferError(String error) {
    if (isInterrupted()) {
      return;
    }

    if (logger.isActivated()) {
      logger.info("Data transfer error: " + error);
    }

    // Close the media session
    closeMediaSession();

    // Terminate session
    terminateSession();

    // Remove the current session
    getImsService().removeSession(this);

    // Notify listeners
    if (!isInterrupted()) {
      for (int j = 0; j < getListeners().size(); j++) {
        ((FileSharingSessionListener) getListeners().get(j))
            .handleTransferError(
                new FileSharingError(FileSharingError.MEDIA_TRANSFER_FAILED, error));
      }
    }
  }

  /** Close the MSRP session */
  private void closeMsrpSession() {
    if (msrpMgr != null) {
      msrpMgr.closeSession();
    }
    if (logger.isActivated()) {
      logger.debug("MSRP session has been closed");
    }
  }

  /** Close media session */
  public void closeMediaSession() {
    // Close MSRP session
    closeMsrpSession();
  }
}
예제 #12
0
/**
 * RCS core service. This service offers a flat API to any other process (activities) to access to
 * RCS features. This service is started automatically at device boot.
 *
 * @author jexa7410
 */
public class RcsCoreService extends Service implements CoreListener {
  /** Service name */
  public static final String SERVICE_NAME = "com.orangelabs.rcs.SERVICE";

  /** Notification ID */
  private static final int SERVICE_NOTIFICATION = 1000;

  /** CPU manager */
  private CpuManager cpuManager = new CpuManager();

  /** IMS API */
  private ImsApiService imsApi = new ImsApiService();

  /** Terms API */
  private TermsApiService termsApi = new TermsApiService();

  /** Presence API */
  private PresenceApiService presenceApi = new PresenceApiService();

  /** Capability API */
  private CapabilityApiService capabilityApi = new CapabilityApiService();

  /** Messaging API */
  private MessagingApiService messagingApi = new MessagingApiService();

  /** Rich call API */
  private RichCallApiService richcallApi = new RichCallApiService();

  /** IP call API */
  private IPCallApiService ipcallApi = new IPCallApiService();

  /** SIP API */
  private SipApiService sipApi = new SipApiService();

  /** Account changed broadcast receiver */
  private AccountChangedReceiver accountChangedReceiver = null;

  /** Account changed broadcast receiver */
  private HttpsProvisioningSMS reconfSMSReceiver = null;

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  @Override
  public void onCreate() {
    // Set application context
    AndroidFactory.setApplicationContext(getApplicationContext());

    // Instantiate the settings manager
    RcsSettings.createInstance(getApplicationContext());

    // Set the logger properties
    Logger.activationFlag = RcsSettings.getInstance().isTraceActivated();
    Logger.traceLevel = RcsSettings.getInstance().getTraceLevel();

    // Set the terminal version
    TerminalInfo.setProductVersion(AppUtils.getApplicationVersion(this));

    // Start the core
    startCore();
  }

  @Override
  public void onDestroy() {
    // Unregister account changed broadcast receiver
    if (accountChangedReceiver != null) {
      try {
        unregisterReceiver(accountChangedReceiver);
      } catch (IllegalArgumentException e) {
        // Nothing to do
      }
    }

    // Unregister SMS receiver for network initiated configuration
    if (reconfSMSReceiver != null) {
      try {
        reconfSMSReceiver.unregisterSmsProvisioningReceiver();
      } catch (IllegalArgumentException e) {
        // Nothing to do
      }
    }

    // Close APIs
    imsApi.close();
    termsApi.close();
    presenceApi.close();
    capabilityApi.close();
    richcallApi.close();
    ipcallApi.close();
    messagingApi.close();
    sipApi.close();

    // Stop the core
    Thread t =
        new Thread() {
          /** Processing */
          public void run() {
            stopCore();
          }
        };
    t.start();
  }

  /** Start core */
  public synchronized void startCore() {
    if (Core.getInstance() != null) {
      // Already started
      return;
    }

    try {
      if (logger.isActivated()) {
        logger.debug("Start RCS core service");
      }

      // Send service intent
      Intent intent = new Intent(ClientApiIntents.SERVICE_STATUS);
      intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STARTING);
      getApplicationContext().sendBroadcast(intent);

      // Terminal version
      if (logger.isActivated()) {
        logger.info("RCS stack release is " + TerminalInfo.getProductVersion());
      }

      // Instantiate the contacts manager
      ContactsManager.createInstance(getApplicationContext());

      // Instantiate the rich messaging history
      RichMessaging.createInstance(getApplicationContext());

      // Instantiate the rich call history
      RichCall.createInstance(getApplicationContext());

      // Instantiate the IP call history
      IPCall.createInstance(getApplicationContext());

      // Create the core
      Core.createCore(this);

      // Start the core
      Core.getInstance().startCore();

      // Create multimedia directory on sdcard
      FileFactory.createDirectory(RcsSettings.getInstance().getPhotoRootDirectory());
      FileFactory.createDirectory(RcsSettings.getInstance().getVideoRootDirectory());
      FileFactory.createDirectory(RcsSettings.getInstance().getFileRootDirectory());

      // Init CPU manager
      cpuManager.init();

      // Register account changed event receiver
      if (accountChangedReceiver == null) {
        accountChangedReceiver = new AccountChangedReceiver();

        // Register account changed broadcast receiver after a timeout of 2s (This is not done
        // immediately, as we do not want to catch
        // the removal of the account (creating and removing accounts is done asynchronously). We
        // can reasonably assume that no
        // RCS account deletion will be done by user during this amount of time, as he just started
        // his service.
        Handler handler = new Handler();
        handler.postDelayed(
            new Runnable() {
              public void run() {
                registerReceiver(
                    accountChangedReceiver,
                    new IntentFilter("android.accounts.LOGIN_ACCOUNTS_CHANGED"));
              }
            },
            2000);
      }

      // Register SMS receiver for network initiated configuration

      if (reconfSMSReceiver == null) {
        reconfSMSReceiver = new HttpsProvisioningSMS(this);
        reconfSMSReceiver.registerSmsProvisioningReceiver(
            Integer.toString(HttpsProvisioningUtils.DEFAULT_SMS_PORT), null, null, null);
      }

      // Show a first notification
      addRcsServiceNotification(false, getString(R.string.rcs_core_loaded));

      // Update GSMA client API
      GsmaUtils.setClientActivationState(getApplicationContext(), true);

      // Send service intent
      intent = new Intent(ClientApiIntents.SERVICE_STATUS);
      intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STARTED);
      getApplicationContext().sendBroadcast(intent);

      if (logger.isActivated()) {
        logger.info("RCS core service started with success");
      }
    } catch (Exception e) {
      // Unexpected error
      if (logger.isActivated()) {
        logger.error("Can't instanciate the RCS core service", e);
      }

      // Send service intent
      Intent intent = new Intent(ClientApiIntents.SERVICE_STATUS);
      intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_FAILED);
      getApplicationContext().sendBroadcast(intent);

      // Show error in notification bar
      addRcsServiceNotification(false, getString(R.string.rcs_core_failed));

      // Exit service
      stopSelf();
    }
  }

  /** Stop core */
  public synchronized void stopCore() {
    if (Core.getInstance() == null) {
      // Already stopped
      return;
    }

    if (logger.isActivated()) {
      logger.debug("Stop RCS core service");
    }

    // Update GSMA client API
    GsmaUtils.setClientActivationState(getApplicationContext(), false);

    // Send service intent
    Intent intent = new Intent(ClientApiIntents.SERVICE_STATUS);
    intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STOPPING);
    getApplicationContext().sendBroadcast(intent);

    // Terminate the core in background
    Core.terminateCore();

    // Close CPU manager
    cpuManager.close();

    // Send service intent
    intent = new Intent(ClientApiIntents.SERVICE_STATUS);
    intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STOPPED);
    getApplicationContext().sendBroadcast(intent);

    if (logger.isActivated()) {
      logger.info("RCS core service stopped with success");
    }
  }

  @Override
  public IBinder onBind(Intent intent) {
    if (IImsApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("IMS API binding");
      }
      return imsApi;
    } else if (ITermsApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("Terms API binding");
      }
      return termsApi;
    } else if (IPresenceApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("Presence API binding");
      }
      return presenceApi;
    } else if (ICapabilityApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("Capability API binding");
      }
      return capabilityApi;
    } else if (IMessagingApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("Messaging API binding");
      }
      return messagingApi;
    } else if (IRichCallApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("Rich call API binding");
      }
      return richcallApi;
    } else if (IIPCallApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("IP call API binding");
      }
      return ipcallApi;
    } else if (ISipApi.class.getName().equals(intent.getAction())) {
      if (logger.isActivated()) {
        logger.debug("SIP API binding");
      }
      return sipApi;
    } else {
      return null;
    }
  }

  /**
   * Add RCS service notification
   *
   * @param state Service state (ON|OFF)
   * @param label Label
   */
  public static void addRcsServiceNotification(boolean state, String label) {
    // Create notification
    Intent intent = new Intent(ClientApiIntents.RCS_SETTINGS);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    PendingIntent contentIntent =
        PendingIntent.getActivity(AndroidFactory.getApplicationContext(), 0, intent, 0);
    int iconId;
    if (state) {
      iconId = R.drawable.rcs_core_notif_on_icon;
    } else {
      iconId = R.drawable.rcs_core_notif_off_icon;
    }
    Notification notif = new Notification(iconId, "", System.currentTimeMillis());
    notif.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_FOREGROUND_SERVICE;
    notif.setLatestEventInfo(
        AndroidFactory.getApplicationContext(),
        AndroidFactory.getApplicationContext().getString(R.string.rcs_core_rcs_notification_title),
        label,
        contentIntent);

    // Send notification
    NotificationManager notificationManager =
        (NotificationManager)
            AndroidFactory.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(SERVICE_NOTIFICATION, notif);
  }

  /*---------------------------- CORE EVENTS ---------------------------*/

  /** Core layer has been started */
  public void handleCoreLayerStarted() {
    if (logger.isActivated()) {
      logger.debug("Handle event core started");
    }

    // Display a notification
    addRcsServiceNotification(false, getString(R.string.rcs_core_started));
  }

  /** Core layer has been terminated */
  public void handleCoreLayerStopped() {
    // Display a notification
    if (logger.isActivated()) {
      logger.debug("Handle event core terminated");
    }
    addRcsServiceNotification(false, getString(R.string.rcs_core_stopped));
  }

  /** Send IMS intent when registered */
  private void sendImsIntentRegistered() {
    // TODO keep only one intent here

    // Send registration intent
    Intent intent = new Intent(ImsApiIntents.IMS_STATUS);
    intent.putExtra("status", true);
    getApplicationContext().sendBroadcast(intent);

    // Send GSMA UI Connector intent
    Intent intentGsma = new Intent(GsmaUiConnector.ACTION_REGISTRATION_CHANGED);
    intentGsma.putExtra(GsmaUiConnector.EXTRA_REGISTRATION_STATUS, true);
    getApplicationContext().sendBroadcast(intentGsma);
  }

  /**
   * Send IMS intent when not registered
   *
   * @param reason Disconnection reason
   */
  private void sendImsIntentNotRegistered(int reason) {
    // TODO keep only one intent here

    // Send registration intent
    Intent intent = new Intent(ImsApiIntents.IMS_STATUS);
    intent.putExtra("status", false);
    intent.putExtra("reason", reason);
    getApplicationContext().sendBroadcast(intent);

    // Send GSMA UI Connector intent
    Intent intentGsma = new Intent(GsmaUiConnector.ACTION_REGISTRATION_CHANGED);
    intentGsma.putExtra(GsmaUiConnector.EXTRA_REGISTRATION_STATUS, false);
    getApplicationContext().sendBroadcast(intentGsma);
  }

  /**
   * Handle "registration successful" event
   *
   * @param registered Registration flag
   */
  public void handleRegistrationSuccessful() {
    if (logger.isActivated()) {
      logger.debug("Handle event registration ok");
    }

    // Send registration intent
    sendImsIntentRegistered();

    // Send registration intent
    Intent intent = new Intent(ClientApiIntents.SERVICE_REGISTRATION);
    intent.putExtra("status", true);
    getApplicationContext().sendStickyBroadcast(intent);

    // Display a notification
    addRcsServiceNotification(true, getString(R.string.rcs_core_ims_connected));
  }

  /** M added to how notification of connecting and disconnecting states during registration */
  /** Handle "try registration" event */
  public void handleTryRegister() {
    if (logger.isActivated()) {
      logger.debug("Handle event try registration");
    }

    // Display a notification
    addRcsServiceNotification(false, getString(R.string.rcs_core_ims_try_connection));
  }

  /** Handle "try registration" event */
  public void handleTryDeregister() {
    if (logger.isActivated()) {
      logger.debug("Handle event try deregistration");
    }

    // Display a notification
    addRcsServiceNotification(false, getString(R.string.rcs_core_ims_try_disconnect));
  }
  /** @ */

  /**
   * Handle "registration failed" event
   *
   * @param error IMS error
   */
  public void handleRegistrationFailed(ImsError error) {
    if (logger.isActivated()) {
      logger.debug("Handle event registration failed");
    }

    // Send registration intent
    sendImsIntentNotRegistered(ImsDisconnectionReason.REGISTRATION_FAILED);

    String reason = "";
    reason += error.getErrorCode() + " : " + error.getErrorMessage();
    // Send registration intent
    Intent intent = new Intent(ClientApiIntents.SERVICE_REGISTRATION);
    intent.putExtra("status", false);
    intent.putExtra("reason", reason);
    getApplicationContext().sendBroadcast(intent);

    // Display a notification
    addRcsServiceNotification(false, getString(R.string.rcs_core_ims_connection_failed));
  }

  /** Handle "registration terminated" event */
  public void handleRegistrationTerminated() {
    if (logger.isActivated()) {
      logger.debug("Handle event registration terminated");
    }

    if (Core.getInstance().getImsModule().getImsConnectionManager().isDisconnectedByBattery()) {
      // Display a notification
      addRcsServiceNotification(false, getString(R.string.rcs_core_ims_battery_disconnected));

      // Send registration intent
      sendImsIntentNotRegistered(ImsDisconnectionReason.BATTERY_LOW);
    } else {
      // Display a notification
      addRcsServiceNotification(false, getString(R.string.rcs_core_ims_disconnected));

      // Send registration intent
      sendImsIntentNotRegistered(ImsDisconnectionReason.SERVICE_TERMINATED);
    }
  }

  /**
   * A new presence sharing notification has been received
   *
   * @param contact Contact
   * @param status Status
   * @param reason Reason
   */
  public void handlePresenceSharingNotification(String contact, String status, String reason) {
    if (logger.isActivated()) {
      logger.debug(
          "Handle event presence sharing notification for "
              + contact
              + " ("
              + status
              + ":"
              + reason
              + ")");
    }

    try {
      // Check if its a notification for a contact or for the end user
      String me = ImsModule.IMS_USER_PROFILE.getPublicUri();
      if (PhoneUtils.compareNumbers(me, contact)) {
        // End user notification
        if (logger.isActivated()) {
          logger.debug("Presence sharing notification for me: by-pass it");
        }
      } else {
        // Update contacts database
        ContactsManager.getInstance().setContactSharingStatus(contact, status, reason);

        // Broadcast intent
        Intent intent = new Intent(PresenceApiIntents.PRESENCE_SHARING_CHANGED);
        intent.putExtra("contact", contact);
        intent.putExtra("status", status);
        intent.putExtra("reason", reason);
        AndroidFactory.getApplicationContext().sendBroadcast(intent);
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Internal exception", e);
      }
    }
  }

  /**
   * A new presence info notification has been received
   *
   * @param contact Contact
   * @param presense Presence info document
   */
  public void handlePresenceInfoNotification(String contact, PidfDocument presence) {
    if (logger.isActivated()) {
      logger.debug("Handle event presence info notification for " + contact);
    }

    try {
      // Test if person item is not null
      Person person = presence.getPerson();
      if (person == null) {
        if (logger.isActivated()) {
          logger.debug("Presence info is empty (i.e. no item person found) for contact " + contact);
        }
        return;
      }

      // Check if its a notification for a contact or for me
      String me = ImsModule.IMS_USER_PROFILE.getPublicUri();
      if (PhoneUtils.compareNumbers(me, contact)) {
        // Notification for me
        presenceInfoNotificationForMe(presence);
      } else {
        // Check that the contact exist in database
        int rcsStatus = ContactsManager.getInstance().getContactSharingStatus(contact);
        if (rcsStatus == -1) {
          if (logger.isActivated()) {
            logger.debug("Contact " + contact + " is not a RCS contact, by-pass the notification");
          }
          return;
        }

        // Notification for a contact
        presenceInfoNotificationForContact(contact, presence);
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Internal exception", e);
      }
    }
  }

  /**
   * A new presence info notification has been received for me
   *
   * @param contact Contact
   * @param presense Presence info document
   */
  public void presenceInfoNotificationForMe(PidfDocument presence) {
    if (logger.isActivated()) {
      logger.debug("Presence info notification for me");
    }

    try {
      // Get the current presence info for me
      PresenceInfo currentPresenceInfo = ContactsManager.getInstance().getMyPresenceInfo();
      if (currentPresenceInfo == null) {
        currentPresenceInfo = new PresenceInfo();
      }

      // Update presence status
      String presenceStatus = PresenceInfo.UNKNOWN;
      Person person = presence.getPerson();
      OverridingWillingness willingness = person.getOverridingWillingness();
      if (willingness != null) {
        if ((willingness.getBasic() != null) && (willingness.getBasic().getValue() != null)) {
          presenceStatus = willingness.getBasic().getValue();
        }
      }
      currentPresenceInfo.setPresenceStatus(presenceStatus);

      // Update the presence info
      currentPresenceInfo.setTimestamp(person.getTimestamp());
      if (person.getNote() != null) {
        currentPresenceInfo.setFreetext(person.getNote().getValue());
      }
      if (person.getHomePage() != null) {
        currentPresenceInfo.setFavoriteLink(new FavoriteLink(person.getHomePage()));
      }

      // Get photo Etag values
      String lastEtag = null;
      String newEtag = null;
      if (person.getStatusIcon() != null) {
        newEtag = person.getStatusIcon().getEtag();
      }
      if (currentPresenceInfo.getPhotoIcon() != null) {
        lastEtag = currentPresenceInfo.getPhotoIcon().getEtag();
      }

      // Test if the photo has been removed
      if ((lastEtag != null) && (person.getStatusIcon() == null)) {
        if (logger.isActivated()) {
          logger.debug("Photo has been removed for me");
        }

        // Update the presence info
        currentPresenceInfo.setPhotoIcon(null);

        // Update EAB provider
        ContactsManager.getInstance().removeMyPhotoIcon();
      } else
      // Test if the photo has been changed
      if ((person.getStatusIcon() != null) && (newEtag != null)) {
        if ((lastEtag == null) || (!lastEtag.equals(newEtag))) {
          if (logger.isActivated()) {
            logger.debug("Photo has changed for me, download it in background");
          }

          // Download the photo in background
          downloadPhotoForMe(presence.getPerson().getStatusIcon().getUrl(), newEtag);
        }
      }

      // Update EAB provider
      ContactsManager.getInstance().setMyInfo(currentPresenceInfo);

      // Broadcast intent
      Intent intent = new Intent(PresenceApiIntents.MY_PRESENCE_INFO_CHANGED);
      getApplicationContext().sendBroadcast(intent);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Internal exception", e);
      }
    }
  }

  /**
   * A new presence info notification has been received for a given contact
   *
   * @param contact Contact
   * @param presense Presence info document
   */
  public void presenceInfoNotificationForContact(String contact, PidfDocument presence) {
    if (logger.isActivated()) {
      logger.debug("Presence info notification for contact " + contact);
    }

    try {
      // Extract number from contact
      String number = PhoneUtils.extractNumberFromUri(contact);

      // Get the current presence info
      ContactInfo currentContactInfo = ContactsManager.getInstance().getContactInfo(contact);
      ContactInfo newContactInfo = currentContactInfo;
      if (currentContactInfo == null) {
        if (logger.isActivated()) {
          logger.warn("Contact " + contact + " not found in EAB: by-pass the notification");
        }
        return;
      }
      PresenceInfo newPresenceInfo = currentContactInfo.getPresenceInfo();
      if (newPresenceInfo == null) {
        newPresenceInfo = new PresenceInfo();
        newContactInfo.setPresenceInfo(newPresenceInfo);
      }

      // Update the current capabilities
      Capabilities capabilities = new Capabilities();
      Vector<Tuple> tuples = presence.getTuplesList();
      for (int i = 0; i < tuples.size(); i++) {
        Tuple tuple = (Tuple) tuples.elementAt(i);

        boolean state = false;
        if (tuple.getStatus().getBasic().getValue().equals("open")) {
          state = true;
        }

        String id = tuple.getService().getId();
        if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_VIDEO_SHARE)) {
          capabilities.setVideoSharingSupport(state);
        } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_IMAGE_SHARE)) {
          capabilities.setImageSharingSupport(state);
        } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_FT)) {
          capabilities.setFileTransferSupport(state);
        } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_CS_VIDEO)) {
          capabilities.setCsVideoSupport(state);
        } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_CHAT)) {
          capabilities.setImSessionSupport(state);
        }
      }
      newContactInfo.setCapabilities(capabilities);

      // Update presence status
      String presenceStatus = PresenceInfo.UNKNOWN;
      Person person = presence.getPerson();
      OverridingWillingness willingness = person.getOverridingWillingness();
      if (willingness != null) {
        if ((willingness.getBasic() != null) && (willingness.getBasic().getValue() != null)) {
          presenceStatus = willingness.getBasic().getValue();
        }
      }
      newPresenceInfo.setPresenceStatus(presenceStatus);

      // Update the presence info
      newPresenceInfo.setTimestamp(person.getTimestamp());
      if (person.getNote() != null) {
        newPresenceInfo.setFreetext(person.getNote().getValue());
      }
      if (person.getHomePage() != null) {
        newPresenceInfo.setFavoriteLink(new FavoriteLink(person.getHomePage()));
      }

      // Update geoloc info
      if (presence.getGeopriv() != null) {
        Geoloc geoloc =
            new Geoloc(
                presence.getGeopriv().getLatitude(),
                presence.getGeopriv().getLongitude(),
                presence.getGeopriv().getAltitude());
        newPresenceInfo.setGeoloc(geoloc);
      }
      newContactInfo.setPresenceInfo(newPresenceInfo);

      // Update contacts database
      ContactsManager.getInstance().setContactInfo(newContactInfo, currentContactInfo);

      // Get photo Etag values
      String lastEtag = ContactsManager.getInstance().getContactPhotoEtag(contact);
      String newEtag = null;
      if (person.getStatusIcon() != null) {
        newEtag = person.getStatusIcon().getEtag();
      }

      // Test if the photo has been removed
      if ((lastEtag != null) && (person.getStatusIcon() == null)) {
        if (logger.isActivated()) {
          logger.debug("Photo has been removed for " + contact);
        }

        // Update contacts database
        ContactsManager.getInstance().setContactPhotoIcon(contact, null);

        // Broadcast intent
        Intent intent = new Intent(PresenceApiIntents.CONTACT_PHOTO_CHANGED);
        intent.putExtra("contact", number);
        AndroidFactory.getApplicationContext().sendBroadcast(intent);
      } else
      // Test if the photo has been changed
      if ((person.getStatusIcon() != null) && (newEtag != null)) {
        if ((lastEtag == null) || (!lastEtag.equals(newEtag))) {
          if (logger.isActivated()) {
            logger.debug("Photo has changed for " + contact + ", download it in background");
          }

          // Download the photo in background
          downloadPhotoForContact(contact, presence.getPerson().getStatusIcon().getUrl(), newEtag);
        }
      }

      // Broadcast intent
      Intent intent = new Intent(PresenceApiIntents.CONTACT_INFO_CHANGED);
      intent.putExtra("contact", number);
      getApplicationContext().sendBroadcast(intent);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Internal exception", e);
      }
    }
  }

  /**
   * Capabilities update notification has been received
   *
   * @param contact Contact
   * @param capabilities Capabilities
   */
  public void handleCapabilitiesNotification(String contact, Capabilities capabilities) {
    if (logger.isActivated()) {
      logger.debug(
          "Handle capabilities update notification for "
              + contact
              + " ("
              + capabilities.toString()
              + ")");
    }

    // Extract number from contact
    String number = PhoneUtils.extractNumberFromUri(contact);

    // Broadcast intent containing the new capabilities
    Intent intent = new Intent(CapabilityApiIntents.CONTACT_CAPABILITIES);
    intent.putExtra("contact", number);
    intent.putExtra("capabilities", capabilities);
    getApplicationContext().sendBroadcast(intent);
    // TODO keep only one intent here

    // Send GSMA UI Connector intent
    Intent intentGsma = new Intent(GsmaUiConnector.ACTION_CAPABILITIES_CHANGED);
    intentGsma.putExtra(GsmaUiConnector.EXTRA_CONTACT, number);
    intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_CHAT, capabilities.isImSessionSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_FT, capabilities.isFileTransferSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_IMAGE_SHARE, capabilities.isImageSharingSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_VIDEO_SHARE, capabilities.isVideoSharingSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_GEOLOCATION_PUSH,
        capabilities.isGeolocationPushSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_CS_VIDEO, capabilities.isCsVideoSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_PRESENCE_DISCOVERY,
        capabilities.isPresenceDiscoverySupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_SOCIAL_PRESENCE, capabilities.isSocialPresenceSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_IPVOICECALL, capabilities.isIPVoiceCallSupported());
    intentGsma.putExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_IPVIDEOCALL, capabilities.isIPVideoCallSupported());
    intentGsma.putStringArrayListExtra(
        GsmaUiConnector.EXTRA_CAPABILITY_EXTENSIONS, capabilities.getSupportedExtensions());
    getApplicationContext().sendBroadcast(intentGsma);
  }

  /**
   * Download photo for me
   *
   * @param url Photo URL
   * @param etag New Etag associated to the photo
   */
  private void downloadPhotoForMe(final String url, final String etag) {
    Thread t =
        new Thread() {
          public void run() {
            try {
              // Download from XDMS
              PhotoIcon icon =
                  Core.getInstance()
                      .getPresenceService()
                      .getXdmManager()
                      .downloadContactPhoto(url, etag);
              if (icon != null) {
                // Update the presence info
                Core.getInstance().getPresenceService().getPresenceInfo().setPhotoIcon(icon);

                // Update contacts database
                ContactsManager.getInstance().setMyPhotoIcon(icon);

                // Broadcast intent
                // TODO : use a specific intent for the end user photo
                Intent intent = new Intent(PresenceApiIntents.MY_PRESENCE_INFO_CHANGED);
                getApplicationContext().sendBroadcast(intent);
              }
            } catch (Exception e) {
              if (logger.isActivated()) {
                logger.error("Internal exception", e);
              }
            }
          }
        };
    t.start();
  }

  /**
   * Download photo for a given contact
   *
   * @param contact Contact
   * @param url Photo URL
   * @param etag New Etag associated to the photo
   */
  private void downloadPhotoForContact(final String contact, final String url, final String etag) {
    Thread t =
        new Thread() {
          public void run() {
            try {
              // Download from XDMS
              PhotoIcon icon =
                  Core.getInstance()
                      .getPresenceService()
                      .getXdmManager()
                      .downloadContactPhoto(url, etag);
              if (icon != null) {
                // Update contacts database
                ContactsManager.getInstance().setContactPhotoIcon(contact, icon);

                // Extract number from contact
                String number = PhoneUtils.extractNumberFromUri(contact);

                // Broadcast intent
                Intent intent = new Intent(PresenceApiIntents.CONTACT_PHOTO_CHANGED);
                intent.putExtra("contact", number);
                getApplicationContext().sendBroadcast(intent);
              }
            } catch (Exception e) {
              if (logger.isActivated()) {
                logger.error("Internal exception", e);
              }
            }
          }
        };
    t.start();
  }

  /**
   * A new presence sharing invitation has been received
   *
   * @param contact Contact
   */
  public void handlePresenceSharingInvitation(String contact) {
    if (logger.isActivated()) {
      logger.debug("Handle event presence sharing invitation");
    }

    // Extract number from contact
    String number = PhoneUtils.extractNumberFromUri(contact);

    // Broadcast intent related to the received invitation
    Intent intent = new Intent(PresenceApiIntents.PRESENCE_INVITATION);
    intent.putExtra("contact", number);
    getApplicationContext().sendBroadcast(intent);
  }

  /**
   * New content sharing transfer invitation
   *
   * @param session Content sharing transfer invitation
   */
  public void handleContentSharingTransferInvitation(ImageTransferSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event content sharing transfer invitation");
    }

    // Broadcast the invitation
    richcallApi.receiveImageSharingInvitation(session);
  }

  /**
   * New content sharing transfer invitation
   *
   * @param session Content sharing transfer invitation
   */
  public void handleContentSharingTransferInvitation(GeolocTransferSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event content sharing transfer invitation");
    }

    // Broadcast the invitation
    richcallApi.receiveGeolocSharingInvitation(session);
  }

  /**
   * New content sharing streaming invitation
   *
   * @param session CSh session
   */
  public void handleContentSharingStreamingInvitation(VideoStreamingSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event content sharing streaming invitation");
    }

    // Broadcast the invitation
    richcallApi.receiveVideoSharingInvitation(session);
  }

  /**
   * New IP call invitation
   *
   * @param session IP call session
   */
  @Override
  public void handleIPCallInvitation(IPCallStreamingSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event IP call invitation");
    }
    // Broadcast the invitation
    ipcallApi.receiveIPCallInvitation(session);
  }

  /**
   * A new file transfer invitation has been received
   *
   * @param session File transfer session
   * @param isGroup is group file transfer
   */
  public void handleFileTransferInvitation(FileSharingSession session, boolean isGroup) {
    if (logger.isActivated()) {
      logger.debug("Handle event file transfer invitation");
    }

    // Broadcast the invitation
    messagingApi.receiveFileTransferInvitation(session, isGroup);
  }

  /**
   * A new file transfer invitation has been received
   *
   * @param session File transfer session
   * @param chatSession Chat session
   */
  public void handle1to1FileTransferInvitation(
      FileSharingSession session, OneOneChatSession chatSession) {
    if (logger.isActivated()) {
      logger.debug("Handle event file transfer invitation from an existing 1-1 chat session");
    }

    // Broadcast the invitation
    messagingApi.receiveFileTransferInvitation(session, chatSession);
  }

  /**
   * A new file transfer invitation has been received and creating a chat session
   *
   * @param session File transfer session
   * @param chatSession Group chat session
   */
  public void handleGroupFileTransferInvitation(
      FileSharingSession session, TerminatingAdhocGroupChatSession chatSession) {
    if (logger.isActivated()) {
      logger.debug("Handle event file transfer invitation from an existing group chat session");
    }

    // Broadcast the invitation
    messagingApi.receiveFileTransferInvitation(session, chatSession);
  }

  /**
   * New one-to-one chat session invitation
   *
   * @param session Chat session
   */
  public void handleOneOneChatSessionInvitation(TerminatingOne2OneChatSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event receive 1-1 chat session invitation");
    }

    // Broadcast the invitation
    messagingApi.receiveOneOneChatInvitation(session);
  }

  /**
   * New ad-hoc group chat session invitation
   *
   * @param session Chat session
   */
  public void handleAdhocGroupChatSessionInvitation(TerminatingAdhocGroupChatSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event receive ad-hoc group chat session invitation");
    }

    // Broadcast the invitation
    messagingApi.receiveGroupChatInvitation(session);
  }

  /**
   * One-to-one chat session extended to a group chat session
   *
   * @param groupSession Group chat session
   * @param oneoneSession 1-1 chat session
   */
  public void handleOneOneChatSessionExtended(
      GroupChatSession groupSession, OneOneChatSession oneoneSession) {
    if (logger.isActivated()) {
      logger.debug("Handle event 1-1 chat session extended");
    }

    // Broadcast the event
    messagingApi.extendOneOneChatSession(groupSession, oneoneSession);
  }

  /**
   * Store and Forward messages session invitation
   *
   * @param session Chat session
   */
  public void handleStoreAndForwardMsgSessionInvitation(
      TerminatingStoreAndForwardMsgSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event S&F messages session invitation");
    }

    // Broadcast the invitation
    messagingApi.receiveOneOneChatInvitation(session);
  }

  /** M: add server date for delivery status @{ */
  /**
   * New message delivery status
   *
   * @param contact Contact
   * @param msgId Message ID
   * @param status Delivery status
   * @param date The server date for delivery status
   */
  public void handleMessageDeliveryStatus(String contact, String msgId, String status, long date) {
    if (logger.isActivated()) {
      logger.debug("Handle message delivery status");
    }

    // Notify listeners
    messagingApi.handleMessageDeliveryStatus(contact, msgId, status, date);
  }
  /**
   * New file delivery status
   *
   * @param ftSessionId File transfer session ID
   * @param status Delivery status
   * @param contact contact who notified delivery
   */
  public void handleFileDeliveryStatus(String ftSessionId, String status, String contact) {
    if (logger.isActivated()) {
      logger.debug(
          "Handle file delivery status: session="
              + ftSessionId
              + " status="
              + status
              + " contact="
              + contact);
    }

    // Notify listeners
    messagingApi.handleFileDeliveryStatus(ftSessionId, status, contact);
  }

  /**
   * New SIP session invitation
   *
   * @param intent Resolved intent
   * @param session SIP session
   */
  public void handleSipSessionInvitation(Intent intent, GenericSipSession session) {
    if (logger.isActivated()) {
      logger.debug("Handle event receive SIP session invitation");
    }

    // Broadcast the invitation
    sipApi.receiveSipSessionInvitation(intent, session);
  }

  /**
   * New SIP instant message received
   *
   * @param intent Resolved intent
   */
  public void handleSipInstantMessageReceived(Intent intent) {
    if (logger.isActivated()) {
      logger.debug("Handle event receive SIP instant message");
    }

    // Broadcast the message
    sipApi.receiveSipInstantMessage(intent);
  }

  /**
   * User terms confirmation request
   *
   * @param remote Remote server
   * @param id Request ID
   * @param type Type of request
   * @param pin PIN number requested
   * @param subject Subject
   * @param text Text
   * @param btnLabelAccept Label of Accept button
   * @param btnLabelReject Label of Reject button
   * @param timeout Timeout request
   */
  public void handleUserConfirmationRequest(
      String remote,
      String id,
      String type,
      boolean pin,
      String subject,
      String text,
      String acceptButtonLabel,
      String rejectButtonLabel,
      int timeout) {
    if (logger.isActivated()) {
      logger.debug("Handle event user terms confirmation request");
    }

    // Notify listeners
    termsApi.receiveTermsRequest(
        remote, id, type, pin, subject, text, acceptButtonLabel, rejectButtonLabel, timeout);
  }

  /**
   * User terms confirmation acknowledge
   *
   * @param remote Remote server
   * @param id Request ID
   * @param status Status
   * @param subject Subject
   * @param text Text
   */
  public void handleUserConfirmationAck(
      String remote, String id, String status, String subject, String text) {
    if (logger.isActivated()) {
      logger.debug("Handle event user terms confirmation ack");
    }

    // Notify listeners
    termsApi.receiveTermsAck(remote, id, status, subject, text);
  }

  /**
   * User terms notification
   *
   * @param remote Remote server
   * @param id Request ID
   * @param subject Subject
   * @param text Text
   * @param btnLabel Label of OK button
   */
  public void handleUserNotification(
      String remote, String id, String subject, String text, String okButtonLabel) {
    if (logger.isActivated()) {
      logger.debug("Handle event user terms notification");
    }

    // Notify listeners
    termsApi.receiveUserNotification(remote, id, subject, text, okButtonLabel);
  }

  /** SIM has changed */
  public void handleSimHasChanged() {
    if (logger.isActivated()) {
      logger.debug("Handle SIM has changed");
    }

    // Restart the RCS service
    LauncherUtils.stopRcsService(getApplicationContext());
    LauncherUtils.launchRcsService(getApplicationContext(), true, false);
  }
}
예제 #13
0
/** Live RTP video player. Only the H264 QCIF format is supported. */
public class LiveVideoPlayer extends IVideoPlayer.Stub
    implements Camera.PreviewCallback, RtpStreamListener {

  /** List of supported video codecs */
  private MediaCodec[] supportedMediaCodecs = null;

  /** Selected video codec */
  private VideoCodec selectedVideoCodec = null;

  /** Video format */
  private VideoFormat videoFormat;

  /** AudioRenderer for RTP stream sharing */
  private VideoRenderer videoRenderer = null;

  /** Local RTP port */
  private int localRtpPort;

  /** RTP sender session */
  private VideoRtpSender rtpSender = null;

  /** RTP media input */
  private MediaRtpInput rtpInput = null;

  /** Is player opened */
  private boolean opened = false;

  /** Is player started */
  private boolean started = false;

  /** Video start time */
  private long videoStartTime = 0L;

  /** Media event listeners */
  private Vector<IVideoEventListener> listeners = new Vector<IVideoEventListener>();

  /** Temporary connection to reserve the port */
  private DatagramConnection temporaryConnection = null;

  /** NAL SPS */
  private byte[] sps = new byte[0];

  /** NAL PPS */
  private byte[] pps = new byte[0];

  /** * Current time stamp */
  private long timeStamp = 0;

  /** NAL initialization */
  private boolean nalInit = false;

  /** Scaling factor for encoding */
  private float scaleFactor = 1;

  /** Source Width - used for resizing */
  private int srcWidth = 0;

  /** Source Height - used for resizing */
  private int srcHeight = 0;

  /** Mirroring (horizontal and vertical) for encoding */
  private boolean mirroring = false;

  /** Orientation header id. */
  private int orientationHeaderId = -1;

  /** Camera ID */
  private int cameraId = CameraOptions.FRONT.getValue();

  /** Video Orientation */
  private Orientation mOrientation = Orientation.NONE;

  /** Frame process */
  private FrameProcess frameProcess;

  /** Frame buffer */
  private FrameBuffer frameBuffer = new FrameBuffer();

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /** Constructor */
  public LiveVideoPlayer() {
    // Set the local RTP port
    localRtpPort = NetworkRessourceManager.generateLocalRtpPort();
    reservePort(localRtpPort);

    // Init codecs
    supportedMediaCodecs = CodecsUtils.getPlayerCodecList();

    // Set the default media codec
    if (supportedMediaCodecs.length > 0) {
      setVideoCodec(supportedMediaCodecs[0]);
    }
  }

  /**
   * Constructor for sharing RTP stream with video renderer
   *
   * @param vr video renderer
   */
  public LiveVideoPlayer(VideoRenderer vr) {
    // Set the local RTP port
    localRtpPort = NetworkRessourceManager.generateLocalRtpPort();
    reservePort(localRtpPort);

    // Get and set locally the audio renderer reference
    videoRenderer = vr;

    // Init codecs
    supportedMediaCodecs = CodecsUtils.getPlayerCodecList();

    // Set the default media codec
    if (supportedMediaCodecs.length > 0) {
      setVideoCodec(supportedMediaCodecs[0]);
    }
  }

  /**
   * Constructor with a list of video codecs
   *
   * @param codecs Ordered list of codecs (preferred codec in first)
   */
  public LiveVideoPlayer(MediaCodec[] codecs) {
    // Set the local RTP port
    localRtpPort = NetworkRessourceManager.generateLocalRtpPort();
    reservePort(localRtpPort);

    // Init codecs
    supportedMediaCodecs = codecs;

    // Set the default media codec
    if (supportedMediaCodecs.length > 0) {
      setVideoCodec(supportedMediaCodecs[0]);
    }
  }

  /**
   * Constructor with a list of video codecs and allowing to share RTP stream with video renderer
   *
   * @param codecs Ordered list of codecs (preferred codec in first)
   * @param vr video renderer
   */
  public LiveVideoPlayer(MediaCodec[] codecs, VideoRenderer vr) {
    // Set the local RTP port
    localRtpPort = NetworkRessourceManager.generateLocalRtpPort();
    reservePort(localRtpPort);

    // Get and set locally the audio renderer reference
    videoRenderer = vr;

    // Init codecs
    supportedMediaCodecs = codecs;

    // Set the default media codec
    if (supportedMediaCodecs.length > 0) {
      setVideoCodec(supportedMediaCodecs[0]);
    }
  }

  /**
   * Returns the local RTP port
   *
   * @return Port
   */
  public int getLocalRtpPort() {
    return localRtpPort;
  }

  /**
   * Reserve a port.
   *
   * @param port Port to reserve
   */
  private void reservePort(int port) {
    if (temporaryConnection == null) {
      try {
        temporaryConnection = NetworkFactory.getFactory().createDatagramConnection();
        temporaryConnection.open(port);
      } catch (IOException e) {
        temporaryConnection = null;
      }
    }
  }

  /** Release the reserved port. */
  private void releasePort() {
    if (temporaryConnection != null) {
      try {
        temporaryConnection.close();
      } catch (IOException e) {
        temporaryConnection = null;
      }
    }
  }

  /**
   * Return the video start time
   *
   * @return Milliseconds
   */
  public long getVideoStartTime() {
    return videoStartTime;
  }

  /**
   * Is player opened
   *
   * @return Boolean
   */
  public boolean isOpened() {
    return opened;
  }

  /**
   * Is player started
   *
   * @return Boolean
   */
  public boolean isStarted() {
    return started;
  }

  /**
   * Open the player
   *
   * @param remoteHost Remote host
   * @param remotePort Remote port
   */
  public void open(String remoteHost, int remotePort) {
    if (opened) {
      // Already opened
      return;
    }

    // Check video codec
    if (selectedVideoCodec == null) {
      notifyPlayerEventError("Video codec not selected");
      return;
    }

    // Init video encoder
    try {
      NativeH264EncoderParams nativeH264EncoderParams = new NativeH264EncoderParams();

      // Codec dimensions
      nativeH264EncoderParams.setFrameWidth(selectedVideoCodec.getWidth());
      nativeH264EncoderParams.setFrameHeight(selectedVideoCodec.getHeight());
      nativeH264EncoderParams.setFrameRate(selectedVideoCodec.getFramerate());
      nativeH264EncoderParams.setBitRate(selectedVideoCodec.getBitrate());

      // Codec profile and level
      nativeH264EncoderParams.setProfilesAndLevel(selectedVideoCodec.getCodecParams());

      // Codec settings optimization
      nativeH264EncoderParams.setEncMode(NativeH264EncoderParams.ENCODING_MODE_STREAMING);
      nativeH264EncoderParams.setSceneDetection(false);

      if (logger.isActivated()) {
        logger.info(
            "Init H264Encoder "
                + selectedVideoCodec.getCodecParams()
                + " "
                + selectedVideoCodec.getWidth()
                + "x"
                + selectedVideoCodec.getHeight()
                + " "
                + selectedVideoCodec.getFramerate()
                + " "
                + selectedVideoCodec.getBitrate());
      }
      int result = NativeH264Encoder.InitEncoder(nativeH264EncoderParams);
      if (result != 0) {
        notifyPlayerEventError("Encoder init failed with error code " + result);
        return;
      }
    } catch (UnsatisfiedLinkError e) {
      notifyPlayerEventError(e.getMessage());
      return;
    }

    // Init the RTP layer
    try {
      releasePort();
      rtpSender = new VideoRtpSender(videoFormat, localRtpPort);
      rtpInput = new MediaRtpInput();
      rtpInput.open();
      if (videoRenderer != null) {
        // The video renderer is supposed to be opened and so we used its RTP stream
        if (logger.isActivated()) {
          logger.debug("Player shares the renderer RTP stream");
        }
        rtpSender.prepareSession(
            rtpInput, remoteHost, remotePort, videoRenderer.getRtpInputStream(), this);
      } else {
        // The video renderer doesn't exist and so we create a new RTP stream
        rtpSender.prepareSession(rtpInput, remoteHost, remotePort, this);
      }

    } catch (Exception e) {
      notifyPlayerEventError(e.getMessage());
      return;
    }

    // Player is opened
    opened = true;
    notifyPlayerEventOpened();
  }

  /** Close the player */
  public void close() {
    if (!opened) {
      // Already closed
      return;
    }
    // Close the RTP layer
    rtpInput.close();
    rtpSender.stopSession();

    try {
      // Close the video encoder
      NativeH264Encoder.DeinitEncoder();
    } catch (UnsatisfiedLinkError e) {
      if (logger.isActivated()) {
        logger.error("Can't close correctly the encoder", e);
      }
    }

    // Player is closed
    opened = false;
    notifyPlayerEventClosed();
    listeners.clear();
  }

  /** Start the player */
  public synchronized void start() {
    if (!opened) {
      // Player not opened
      return;
    }

    if (started) {
      // Already started
      return;
    }

    // Init NAL
    if (!initNAL()) {
      return;
    }
    nalInit = false;
    timeStamp = 0;

    // Start RTP layer
    rtpSender.startSession();

    // Player is started
    videoStartTime = SystemClock.uptimeMillis();
    started = true;
    frameProcess = new FrameProcess(selectedVideoCodec.getFramerate());
    frameProcess.start();
    notifyPlayerEventStarted();
  }

  /**
   * Init sps and pps
   *
   * @return true if done
   */
  private boolean initNAL() {
    boolean ret = initOneNAL();
    if (ret) {
      ret = initOneNAL();
    }
    return ret;
  }

  /**
   * Init sps or pps
   *
   * @return true if done
   */
  private boolean initOneNAL() {
    byte[] nal = NativeH264Encoder.getNAL();
    if ((nal != null) && (nal.length > 0)) {
      int type = (nal[0] & 0x1f);
      if (type == JavaPacketizer.AVC_NALTYPE_SPS) {
        sps = nal;
        return true;
      } else if (type == JavaPacketizer.AVC_NALTYPE_PPS) {
        pps = nal;
        return true;
      }
    }
    return false;
  }

  /** Stop the player */
  public void stop() {
    if (!opened) {
      // Player not opened
      return;
    }

    if (!started) {
      // Already stopped
      return;
    }

    // Player is stopped
    videoStartTime = 0L;
    started = false;
    try {
      frameProcess.interrupt();
    } catch (Exception e) {
      // Nothing to do
    }
    notifyPlayerEventStopped();
  }

  /**
   * Add a media event listener
   *
   * @param listener Media event listener
   */
  public void addListener(IVideoEventListener listener) {
    listeners.addElement(listener);
  }

  /** Remove all media event listeners */
  public void removeAllListeners() {
    listeners.removeAllElements();
  }

  /**
   * Get supported video codecs
   *
   * @return media Codecs list
   */
  public MediaCodec[] getSupportedVideoCodecs() {
    return supportedMediaCodecs;
  }

  /**
   * Get video codec
   *
   * @return Video codec
   */
  public MediaCodec getVideoCodec() {
    if (selectedVideoCodec == null) {
      return null;
    } else {
      return selectedVideoCodec.getMediaCodec();
    }
  }

  /**
   * Get video codec width
   *
   * @return Width
   */
  public int getVideoCodecWidth() {
    if (selectedVideoCodec == null) {
      return H264Config.VIDEO_WIDTH;
    } else {
      return new VideoCodec(selectedVideoCodec.getMediaCodec()).getWidth();
    }
  }

  /**
   * Get video codec height
   *
   * @return Height
   */
  public int getVideoCodecHeight() {
    if (selectedVideoCodec == null) {
      return H264Config.VIDEO_HEIGHT;
    } else {
      return new VideoCodec(selectedVideoCodec.getMediaCodec()).getHeight();
    }
  }

  /**
   * Set video codec
   *
   * @param mediaCodec Video codec
   */
  public void setVideoCodec(MediaCodec mediaCodec) {
    if (VideoCodec.checkVideoCodec(supportedMediaCodecs, new VideoCodec(mediaCodec))) {
      VideoCodec codec = new VideoCodec(mediaCodec);
      if (codec.getHeight() == 0 || codec.getWidth() == 0) {
        selectedVideoCodec =
            new VideoCodec(
                codec.getCodecName(),
                codec.getPayload(),
                codec.getClockRate(),
                codec.getCodecParams(),
                codec.getFramerate(),
                codec.getBitrate(),
                H264Config.QCIF_WIDTH,
                H264Config.QCIF_HEIGHT);
      } else {
        selectedVideoCodec = codec;
      }
      videoFormat = (VideoFormat) MediaRegistry.generateFormat(mediaCodec.getCodecName());
    } else {
      notifyPlayerEventError("Codec not supported");
    }
  }

  /**
   * Set extension header orientation id
   *
   * @param headerId extension header orientation id
   */
  public void setOrientationHeaderId(int headerId) {
    this.orientationHeaderId = headerId;
  }

  /**
   * Set camera ID
   *
   * @param cameraId Camera ID
   */
  public void setCameraId(int cameraId) {
    this.cameraId = cameraId;
  }

  /**
   * Set video orientation
   *
   * @param orientation
   */
  public void setOrientation(Orientation orientation) {
    mOrientation = orientation;
  }

  /**
   * Set the scaling factor
   *
   * @param scaleFactor New scaling factor
   */
  public void setScalingFactor(float scaleFactor) {
    this.scaleFactor = scaleFactor;
    this.srcWidth = 0;
    this.srcHeight = 0;
  }

  /**
   * Set the source dimension for resizing
   *
   * @param srcWidth
   * @param srcHeight
   */
  public void activateResizing(int srcWidth, int srcHeight) {
    this.srcWidth = srcWidth;
    this.srcHeight = srcHeight;
    this.scaleFactor = 1;
  }

  /**
   * Set the mirroring value
   *
   * @param mirroring New mirroring value
   */
  public void setMirroring(boolean mirroring) {
    this.mirroring = mirroring;
  }

  /** Notify RTP aborted */
  public void rtpStreamAborted() {
    notifyPlayerEventError("RTP session aborted");
  }

  /** Notify player event started */
  private void notifyPlayerEventStarted() {
    if (logger.isActivated()) {
      logger.debug("Player is started");
    }
    Iterator<IVideoEventListener> ite = listeners.iterator();
    while (ite.hasNext()) {
      try {
        ((IVideoEventListener) ite.next()).mediaStarted();
      } catch (RemoteException e) {
        if (logger.isActivated()) {
          logger.error("Can't notify listener", e);
        }
      }
    }
  }

  /** Notify player event stopped */
  private void notifyPlayerEventStopped() {
    if (logger.isActivated()) {
      logger.debug("Player is stopped");
    }
    Iterator<IVideoEventListener> ite = listeners.iterator();
    while (ite.hasNext()) {
      try {
        ((IVideoEventListener) ite.next()).mediaStopped();
      } catch (RemoteException e) {
        if (logger.isActivated()) {
          logger.error("Can't notify listener", e);
        }
      }
    }
  }

  /** Notify player event opened */
  private void notifyPlayerEventOpened() {
    if (logger.isActivated()) {
      logger.debug("Player is opened");
    }
    Iterator<IVideoEventListener> ite = listeners.iterator();
    while (ite.hasNext()) {
      try {
        ((IVideoEventListener) ite.next()).mediaOpened();
      } catch (RemoteException e) {
        if (logger.isActivated()) {
          logger.error("Can't notify listener", e);
        }
      }
    }
  }

  /** Notify player event closed */
  private void notifyPlayerEventClosed() {
    if (logger.isActivated()) {
      logger.debug("Player is closed");
    }
    Iterator<IVideoEventListener> ite = listeners.iterator();
    while (ite.hasNext()) {
      try {
        ((IVideoEventListener) ite.next()).mediaClosed();
      } catch (RemoteException e) {
        if (logger.isActivated()) {
          logger.error("Can't notify listener", e);
        }
      }
    }
  }

  /** Notify player event error */
  private void notifyPlayerEventError(String error) {
    if (logger.isActivated()) {
      logger.debug("Player error: " + error);
    }

    Iterator<IVideoEventListener> ite = listeners.iterator();
    while (ite.hasNext()) {
      try {
        ((IVideoEventListener) ite.next()).mediaError(error);
      } catch (RemoteException e) {
        if (logger.isActivated()) {
          logger.error("Can't notify listener", e);
        }
      }
    }
  }

  /**
   * Preview frame from the camera
   *
   * @param data Frame
   * @param camera Camera
   */
  public void onPreviewFrame(byte[] data, Camera camera) {
    if (!started) {
      return;
    }
    frameBuffer.setData(data);
  };

  /**
   * encode a buffer and add in RTP input
   *
   * @param data
   */
  private void encode(byte[] data) {
    // Set timestamp
    timeStamp = SystemClock.uptimeMillis() - videoStartTime;

    // Encode frame
    byte[] encoded;
    if (frameBuffer.dataSrcWidth != 0 && frameBuffer.dataSrcHeight != 0) {
      encoded =
          NativeH264Encoder.ResizeAndEncodeFrame(
              data, timeStamp, mirroring, frameBuffer.dataSrcWidth, frameBuffer.dataSrcHeight);
    } else {
      encoded =
          NativeH264Encoder.EncodeFrame(data, timeStamp, mirroring, frameBuffer.dataScaleFactor);
    }
    int encodeResult = NativeH264Encoder.getLastEncodeStatus();
    if ((encodeResult == 0) && (encoded.length > 0)) {
      // Send SPS/PPS if IDR or first frame
      if (!nalInit || isIdrFrame(encoded)) {
        rtpInput.addFrame(sps, timeStamp);
        rtpInput.addFrame(pps, timeStamp);
        nalInit = true;
      }

      VideoOrientation videoOrientation = null;
      if (orientationHeaderId > 0) {
        videoOrientation =
            new VideoOrientation(
                orientationHeaderId, CameraOptions.convert(cameraId), mOrientation);
      }
      rtpInput.addFrame(encoded, timeStamp, videoOrientation);
    }
  }

  /**
   * Chech if the frame is IDR
   *
   * @param encodedFrame the encoded frame
   * @return true if IDR
   */
  private boolean isIdrFrame(byte[] encodedFrame) {
    if ((encodedFrame != null) && (encodedFrame.length > 0)) {
      NalUnitHeader header = NalUnitHeader.extract(encodedFrame);
      return header.getNalUnitType() == NalUnitType.CODE_SLICE_IDR_PICTURE;
    }
    return false;
  }

  /** Frame process */
  private class FrameProcess extends Thread {

    /** Time between two frame */
    private int interframe = 1000 / 15;

    /**
     * Constructor
     *
     * @param framerate
     */
    public FrameProcess(int framerate) {
      super();
      interframe = 1000 / framerate;
    }

    @Override
    public void run() {
      byte[] frameData = null;
      while (started) {
        long time = System.currentTimeMillis();

        // Encode
        frameData = frameBuffer.getData();
        if (frameData != null) {
          encode(frameData);
        }

        // Sleep between frames if necessary
        long delta = System.currentTimeMillis() - time;
        if (delta < interframe) {
          try {
            Thread.sleep((interframe - delta) - (((interframe - delta) * 10) / 100));
          } catch (InterruptedException e) {
          }
        }
      }
    }
  }

  /** Frame buffer */
  private class FrameBuffer {
    /** Data */
    private byte[] data = null;

    /** Scaling factor for encoding */
    public float dataScaleFactor = 1;

    /** Source Width - used for resizing */
    public int dataSrcWidth = 0;

    /** Source Height - used for resizing */
    public int dataSrcHeight = 0;

    /**
     * Get the data
     *
     * @return data
     */
    public synchronized byte[] getData() {
      return data;
    }

    /**
     * Set the data
     *
     * @param data
     */
    public synchronized void setData(byte[] data) {
      this.data = data;

      // Update resizing / scaling values
      this.dataScaleFactor = scaleFactor;
      this.dataSrcWidth = srcWidth;
      this.dataSrcHeight = srcHeight;
    }
  }

  /** Media RTP input */
  private static class MediaRtpInput implements MediaInput {
    /** Received frames */
    private FifoBuffer fifo = null;

    /** Constructor */
    public MediaRtpInput() {}

    /**
     * Add a new video frame
     *
     * @param data Data
     * @param timestamp Timestamp
     * @param marker Marker bit
     */
    public void addFrame(byte[] data, long timestamp, VideoOrientation videoOrientation) {
      if (fifo != null) {
        VideoSample sample = new VideoSample(data, timestamp, videoOrientation);
        fifo.addObject(sample);
      }
    }

    /**
     * Add a new video frame
     *
     * @param data Data
     * @param timestamp Timestamp
     * @param marker Marker bit
     */
    public void addFrame(byte[] data, long timestamp) {
      addFrame(data, timestamp, null);
    }

    /** Open the player */
    public void open() {
      fifo = new FifoBuffer();
    }

    /** Close the player */
    public void close() {
      if (fifo != null) {
        fifo.close();
        fifo = null;
      }
    }

    /**
     * Read a media sample (blocking method)
     *
     * @return Media sample
     * @throws MediaException
     */
    public VideoSample readSample() throws MediaException {
      try {
        if (fifo != null) {
          return (VideoSample) fifo.getObject();
        } else {
          throw new MediaException("Media input not opened");
        }
      } catch (Exception e) {
        throw new MediaException("Can't read media sample");
      }
    }
  }
}
예제 #14
0
/**
 * Terminating one-to-one chat session
 *
 * @author jexa7410
 */
public class TerminatingOne2OneChatSession extends OneOneChatSession {
  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param parent IMS service
   * @param invite Initial INVITE request
   */
  public TerminatingOne2OneChatSession(ImsService parent, SipRequest invite) {
    super(parent, PhoneUtils.extractNumberFromUri(SipUtils.getAssertedIdentity(invite)));

    // Set first message
    InstantMessage firstMsg = ChatUtils.getFirstMessage(invite);
    setFirstMesssage(firstMsg);

    // Create dialog path
    createTerminatingDialogPath(invite);

    // Set contribution ID
    String id = ChatUtils.getContributionId(invite);
    setContributionID(id);
    if (RcsSettings.getInstance().isCPMSupported()) {
      if (logger.isActivated()) {
        logger.info("TerminatingFOne2OneSession1  CPMS");
      }
      setConversationID(ChatUtils.getCoversationId(invite));
      setInReplyID(ChatUtils.getInReplyId(invite));
    }

    if (logger.isActivated()) {
      logger.info("TerminatingOne2OneChatSession From: " + ChatUtils.getFromAias(invite));
      logger.info("TerminatingOne2OneChatSession Display name: " + this.getRemoteDisplayName());
    }
    setRemoteDisplayName(this.getRemoteDisplayName());
  }

  /** Background processing */
  public void run() {
    try {
      if (logger.isActivated()) {
        logger.info("Initiate a new 1-1 chat session as terminating");
      }

      // Send message delivery report if requested
      if ((ChatUtils.isImdnDeliveredRequested(getDialogPath().getInvite()))
          || (ChatUtils.isFileTransferOverHttp(getDialogPath().getInvite()))) {
        // Check notification disposition
        String msgId = ChatUtils.getMessageId(getDialogPath().getInvite());
        if (msgId != null) {
          // Send message delivery status via a SIP MESSAGE
          getImdnManager()
              .sendMessageDeliveryStatusImmediately(
                  getDialogPath().getRemoteParty(),
                  msgId,
                  ImdnDocument.DELIVERY_STATUS_DELIVERED,
                  SipUtils.getRemoteInstanceID(getDialogPath().getInvite()));
        }
      }

      // Check if Auto-accept (FT HTTP force auto-accept for the chat session)
      if (RcsSettings.getInstance().isChatAutoAccepted()
          || ChatUtils.getHttpFTInfo(getDialogPath().getInvite()) != null) {
        if (logger.isActivated()) {
          logger.info("Auto accept chat invitation");
        }
      } else {
        if (logger.isActivated()) {
          logger.info("Accept manually chat invitation");
        }

        // Send a 180 Ringing response
        send180Ringing(getDialogPath().getInvite(), getDialogPath().getLocalTag());

        // Wait invitation answer
        int answer = waitInvitationAnswer();
        if (answer == ImsServiceSession.INVITATION_REJECTED) {
          if (logger.isActivated()) {
            logger.info("Session has been rejected by user");
          }

          // Remove the current session
          getImsService().removeSession(this);

          // Notify listeners
          for (int i = 0; i < getListeners().size(); i++) {
            getListeners().get(i).handleSessionAborted(ImsServiceSession.TERMINATION_BY_USER);
          }
          return;
        } else if (answer == ImsServiceSession.INVITATION_NOT_ANSWERED) {
          if (logger.isActivated()) {
            logger.info("Session has been rejected on timeout");
          }

          // Ringing period timeout
          send486Busy(getDialogPath().getInvite(), getDialogPath().getLocalTag());

          // Remove the current session
          getImsService().removeSession(this);

          // Notify listeners
          for (int i = 0; i < getListeners().size(); i++) {
            getListeners().get(i).handleSessionAborted(ImsServiceSession.TERMINATION_BY_TIMEOUT);
          }
          return;
        } else if (answer == ImsServiceSession.INVITATION_CANCELED) {
          if (logger.isActivated()) {
            logger.info("Session has been canceled");
          }
          return;
        }
      }

      // Parse the remote SDP part
      String remoteSdp = getDialogPath().getInvite().getSdpContent();
      SdpParser parser = new SdpParser(remoteSdp.getBytes());
      Vector<MediaDescription> media = parser.getMediaDescriptions();
      MediaDescription mediaDesc = media.elementAt(0);
      MediaAttribute attr1 = mediaDesc.getMediaAttribute("path");
      String remotePath = attr1.getValue();
      String remoteHost = SdpUtils.extractRemoteHost(parser.sessionDescription, mediaDesc);
      int remotePort = mediaDesc.port;

      // Extract the "setup" parameter
      String remoteSetup = "passive";
      MediaAttribute attr2 = mediaDesc.getMediaAttribute("setup");
      if (attr2 != null) {
        remoteSetup = attr2.getValue();
      }
      if (logger.isActivated()) {
        logger.info("Remote setup attribute is " + remoteSetup);
      }

      // Set setup mode
      String localSetup = createSetupAnswer(remoteSetup);
      if (logger.isActivated()) {
        logger.debug("Local setup attribute is " + localSetup);
      }

      // Set local port
      int localMsrpPort;
      if (localSetup.equals("active")) {
        localMsrpPort = 9; // See RFC4145, Page 4
      } else {
        localMsrpPort = getMsrpMgr().getLocalMsrpPort();
      }

      // Build SDP part
      String ntpTime = SipUtils.constructNTPtime(System.currentTimeMillis());
      String ipAddress = getDialogPath().getSipStack().getLocalIpAddress();
      String sdp = null;
      if (isSecureProtocolMessage()) {
        sdp =
            "v=0"
                + SipUtils.CRLF
                + "o=- "
                + ntpTime
                + " "
                + ntpTime
                + " "
                + SdpUtils.formatAddressType(ipAddress)
                + SipUtils.CRLF
                + "s=-"
                + SipUtils.CRLF
                + "c="
                + SdpUtils.formatAddressType(ipAddress)
                + SipUtils.CRLF
                + "t=0 0"
                + SipUtils.CRLF
                + "m=message "
                + localMsrpPort
                + " "
                + getMsrpMgr().getLocalSocketProtocol()
                + " *"
                + SipUtils.CRLF
                + "a=accept-types:"
                + getAcceptTypes()
                + SipUtils.CRLF
                + "a=accept-wrapped-types:"
                + getWrappedTypes()
                + SipUtils.CRLF
                + "a=setup:"
                + localSetup
                + SipUtils.CRLF
                + "a=path:"
                + getMsrpMgr().getLocalMsrpPath()
                + SipUtils.CRLF
                + "a=fingerprint:"
                + KeyStoreManager.getFingerPrint()
                + SipUtils.CRLF
                + "a=sendrecv"
                + SipUtils.CRLF;
      } else {
        sdp =
            "v=0"
                + SipUtils.CRLF
                + "o=- "
                + ntpTime
                + " "
                + ntpTime
                + " "
                + SdpUtils.formatAddressType(ipAddress)
                + SipUtils.CRLF
                + "s=-"
                + SipUtils.CRLF
                + "c="
                + SdpUtils.formatAddressType(ipAddress)
                + SipUtils.CRLF
                + "t=0 0"
                + SipUtils.CRLF
                + "m=message "
                + localMsrpPort
                + " "
                + getMsrpMgr().getLocalSocketProtocol()
                + " *"
                + SipUtils.CRLF
                + "a=accept-types:"
                + getAcceptTypes()
                + SipUtils.CRLF
                + "a=accept-wrapped-types:"
                + getWrappedTypes()
                + SipUtils.CRLF
                + "a=setup:"
                + localSetup
                + SipUtils.CRLF
                + "a=path:"
                + getMsrpMgr().getLocalMsrpPath()
                + SipUtils.CRLF
                + "a=sendrecv"
                + SipUtils.CRLF;
      }

      // Set the local SDP part in the dialog path
      getDialogPath().setLocalContent(sdp);

      // Test if the session should be interrupted
      if (isInterrupted()) {
        if (logger.isActivated()) {
          logger.info("Session has been interrupted: end of processing");
        }
        return;
      }

      // Create the MSRP server session
      if (localSetup.equals("passive")) {
        // Passive mode: client wait a connection
        MsrpSession session = getMsrpMgr().createMsrpServerSession(remotePath, this);
        session.setFailureReportOption(false);
        session.setSuccessReportOption(false);

        // Open the connection
        Thread thread =
            new Thread() {
              public void run() {
                try {
                  // Open the MSRP session
                  getMsrpMgr().openMsrpSession();

                  // Send an empty packet
                  sendEmptyDataChunk();
                } catch (IOException e) {
                  if (logger.isActivated()) {
                    logger.error("Can't create the MSRP server session", e);
                  }
                }
              }
            };
        thread.start();
      }

      // Create a 200 OK response
      if (logger.isActivated()) {
        logger.info("Send 200 OK");
      }
      SipResponse resp = null;
      if (!RcsSettings.getInstance().isCPMSupported()) {
        resp = SipMessageFactory.create200OkInviteResponse(getDialogPath(), getFeatureTags(), sdp);
      } else {
        if (logger.isActivated()) {
          logger.info("TerminatingFOne2OneSession2  CPMS");
        }
        resp =
            SipMessageFactory.createCpm200OkInviteResponse(
                getDialogPath(), getCpimFeatureTags(), sdp);
      }

      if (RcsSettings.getInstance().isCPMSupported()) {
        if (logger.isActivated()) {
          logger.info("TerminatingFOne2OneSession3  CPMS");
        }
        if (getContributionID() != null) {
          resp.addHeader(ChatUtils.HEADER_CONTRIBUTION_ID, getContributionID());
        }
        if (getConversationID() != null) {
          resp.addHeader(ChatUtils.HEADER_CONVERSATION_ID, getConversationID());
        }
        if (getInReplyID() != null) {
          resp.addHeader(ChatUtils.HEADER_INREPLY_TO_CONTRIBUTION_ID, getInReplyID());
        }
      }

      // The signalisation is established
      getDialogPath().sigEstablished();

      // Send response
      SipTransactionContext ctx =
          getImsService().getImsModule().getSipManager().sendSipMessageAndWait(resp);

      // Analyze the received response
      if (ctx.isSipAck()) {
        // ACK received
        if (logger.isActivated()) {
          logger.info("ACK request received");
        }

        // The session is established
        getDialogPath().sessionEstablished();

        // Create the MSRP client session
        if (localSetup.equals("active")) {
          // Active mode: client should connect
          MsrpSession session =
              getMsrpMgr().createMsrpClientSession(remoteHost, remotePort, remotePath, this);
          session.setFailureReportOption(false);
          session.setSuccessReportOption(false);

          // Open the MSRP session
          getMsrpMgr().openMsrpSession();

          // Send an empty packet
          sendEmptyDataChunk();
        }

        // Notify listeners
        for (int i = 0; i < getListeners().size(); i++) {
          getListeners().get(i).handleSessionStarted();
        }

        if (logger.isActivated()) {
          logger.info(
              "ABC ACK request received Dialog expire time: "
                  + getDialogPath().getSessionExpireTime());
        }

        // Start session timer
        if (getSessionTimerManager().isSessionTimerActivated(resp)) {
          getSessionTimerManager()
              .start(SessionTimerManager.UAS_ROLE, getDialogPath().getSessionExpireTime());
        }

        // Start the activity manager
        getActivityManager().start();

      } else {
        if (logger.isActivated()) {
          logger.info("No ACK received for INVITE");
        }

        // No response received: timeout
        handleError(new ChatError(ChatError.SESSION_INITIATION_FAILED));
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(new ChatError(ChatError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }
}
예제 #15
0
/**
 * IMS service session
 *
 * @author jexa7410
 */
public abstract class ImsServiceSession extends Thread {
  /** Session invitation status */
  public static final int INVITATION_NOT_ANSWERED = 0;

  public static final int INVITATION_ACCEPTED = 1;
  public static final int INVITATION_REJECTED = 2;
  public static final int INVITATION_CANCELED = 3;

  /** Session termination reason */
  public static final int TERMINATION_BY_SYSTEM = 0;

  public static final int TERMINATION_BY_USER = 1;
  public static final int TERMINATION_BY_TIMEOUT = 2;

  /** IMS service */
  private ImsService imsService;

  private NetworkSwitchInfo netSwitchInfo = new NetworkSwitchInfo();

  /** Session ID */
  private String sessionId = SessionIdGenerator.getNewId();

  /** Remote contact */
  private String contact;

  /** Remote display name */
  private String remoteDisplayName = null;

  /** Conversation ID */
  private static String mConversationId = "bcc642fe6a5347a64deb882d886c2c7a";

  /** Remote display name */
  private String remoteGroupDisplayName = null;

  /** Dialog path */
  private SipDialogPath dialogPath = null;

  /** Authentication agent */
  private SessionAuthenticationAgent authenticationAgent;

  /** Session invitation status */
  protected int invitationStatus = INVITATION_NOT_ANSWERED;

  /** Wait user answer for session invitation */
  protected Object waitUserAnswer = new Object();

  /** Session listeners */
  private Vector<ImsSessionListener> listeners = new Vector<ImsSessionListener>();

  /** Session timer manager */
  private SessionTimerManager sessionTimer = new SessionTimerManager(this);

  /** Update session manager */
  protected UpdateSessionManager updateMgr;

  /** Ringing period (in seconds) */
  private int ringingPeriod = RcsSettings.getInstance().getRingingPeriod();

  /** Session interrupted flag */
  private boolean sessionInterrupted = false;

  /** Session terminated by remote flag */
  private boolean sessionTerminatedByRemote = false;

  /** The logger */
  private Logger logger = Logger.getLogger(this.getClass().getName());

  /**
   * Constructor
   *
   * @param parent IMS service
   * @param contact Remote contact
   */
  public ImsServiceSession(ImsService imsService, String contact) {
    this.imsService = imsService;
    this.contact = contact;
    this.authenticationAgent = new SessionAuthenticationAgent(imsService.getImsModule());
    this.updateMgr = new UpdateSessionManager(this);
  }

  /** Create originating dialog path */
  public void createOriginatingDialogPath() {
    // Set Call-Id
    String callId = getImsService().getImsModule().getSipManager().getSipStack().generateCallId();

    // Set the route path
    Vector<String> route =
        getImsService().getImsModule().getSipManager().getSipStack().getServiceRoutePath();

    // Create a dialog path
    dialogPath =
        new SipDialogPath(
            getImsService().getImsModule().getSipManager().getSipStack(),
            callId,
            1,
            getRemoteContact(),
            ImsModule.IMS_USER_PROFILE.getPublicAddress(),
            getRemoteContact(),
            route);

    // Set the authentication agent in the dialog path
    dialogPath.setAuthenticationAgent(getAuthenticationAgent());
  }

  /** M: Added to resolve the rich call 403 error.@{ */
  /** Create originating dialog path */
  public void createOriginatingDialogPath(String callId) {
    logger.debug("createOriginatingDialogPath(), callId = " + callId);
    // Set the route path
    Vector<String> route =
        getImsService().getImsModule().getSipManager().getSipStack().getServiceRoutePath();

    // Create a dialog path
    dialogPath =
        new SipDialogPath(
            getImsService().getImsModule().getSipManager().getSipStack(),
            callId,
            1,
            getRemoteContact(),
            ImsModule.IMS_USER_PROFILE.getPublicUri(),
            getRemoteContact(),
            route);

    // Set the authentication agent in the dialog path
    dialogPath.setAuthenticationAgent(getAuthenticationAgent());
  }
  /** @} */

  /**
   * Create terminating dialog path
   *
   * @param invite Incoming invite
   */
  public void createTerminatingDialogPath(SipRequest invite) {
    // Set the call-id
    String callId = invite.getCallId();

    // Set target
    String target = invite.getContactURI();

    // Set local party
    String localParty = invite.getTo();

    // Set remote party
    String remoteParty = invite.getFrom();

    // Get the CSeq value
    long cseq = invite.getCSeq();

    // Set the route path with the Record-Route
    Vector<String> route = SipUtils.routeProcessing(invite, false);

    // Create a dialog path
    dialogPath =
        new SipDialogPath(
            getImsService().getImsModule().getSipManager().getSipStack(),
            callId,
            cseq,
            target,
            localParty,
            remoteParty,
            route);

    // Set the INVITE request
    dialogPath.setInvite(invite);

    // Set the remote tag
    dialogPath.setRemoteTag(invite.getFromTag());

    // Set the remote content part
    dialogPath.setRemoteContent(invite.getContent());

    // Set the session timer expire
    dialogPath.setSessionExpireTime(invite.getSessionTimerExpire());
  }

  /**
   * Add a listener for receiving events
   *
   * @param listener Listener
   */
  public void addListener(ImsSessionListener listener) {
    listeners.add(listener);
  }

  /** Remove a listener */
  public void removeListener(ImsSessionListener listener) {
    listeners.remove(listener);
  }

  /** Remove all listeners */
  public void removeListeners() {
    listeners.removeAllElements();
  }

  /**
   * Returns the event listeners
   *
   * @return Listeners
   */
  public Vector<ImsSessionListener> getListeners() {
    return listeners;
  }

  /**
   * Get the session timer manager
   *
   * @return Session timer manager
   */
  public SessionTimerManager getSessionTimerManager() {
    return sessionTimer;
  }

  /**
   * Get the update session manager
   *
   * @return UpdateSessionManager
   */
  public UpdateSessionManager getUpdateSessionManager() {
    return updateMgr;
  }

  /**
   * Is behind a NAT
   *
   * @return Boolean
   */
  public boolean isBehindNat() {
    return getImsService().getImsModule().getCurrentNetworkInterface().isBehindNat();
  }

  public boolean isSecureProtocolMessage() {
    return getImsService().getImsModule().getCurrentNetworkInterface().getIsSecureProtocol();
  }

  /** Start the session in background */
  public void startSession() {
    // Add the session in the session manager
    imsService.addSession(this);

    // Start the session
    start();
  }

  /**
   * Return the IMS service
   *
   * @return IMS service
   */
  public ImsService getImsService() {
    return imsService;
  }

  /**
   * Return the session ID
   *
   * @return Session ID
   */
  public String getSessionID() {
    return sessionId;
  }

  /**
   * Returns the remote contact
   *
   * @return String
   */
  public String getRemoteContact() {
    return contact;
  }

  /**
   * Returns display name of the remote contact
   *
   * @return String
   */
  public String getRemoteDisplayName() {
    if (getDialogPath() == null) {
      return remoteDisplayName;
    } else {
      return SipUtils.getDisplayNameFromUri(getDialogPath().getInvite().getFrom());
    }
  }

  /**
   * Returns display name of the remote contact
   *
   * @return String
   */
  public String getGroupRemoteDisplayName() {
    return remoteGroupDisplayName;
  }
  /**
   * Set display name of the remote contact
   *
   * @param String
   */
  public void setGroupRemoteDisplayName(String remoteGroupDisplayName) {
    this.remoteGroupDisplayName = remoteGroupDisplayName;
  }

  /**
   * Set display name of the remote contact
   *
   * @param String
   */
  public void setRemoteDisplayName(String remoteDisplayName) {
    this.remoteDisplayName = remoteDisplayName;
  }

  /**
   * Get the dialog path of the session
   *
   * @return Dialog path object
   */
  public SipDialogPath getDialogPath() {
    return dialogPath;
  }

  /**
   * Set the dialog path of the session
   *
   * @param dialog Dialog path
   */
  public void setDialogPath(SipDialogPath dialog) {
    dialogPath = dialog;
  }

  /**
   * Returns the authentication agent
   *
   * @return Authentication agent
   */
  public SessionAuthenticationAgent getAuthenticationAgent() {
    return authenticationAgent;
  }

  /**
   * Reject the session invitation
   *
   * @param code Error code
   */
  public void rejectSession(int code) {
    if (logger.isActivated()) {
      logger.debug("Session invitation has been rejected");
    }
    invitationStatus = INVITATION_REJECTED;

    // Unblock semaphore
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }

    // Decline the invitation
    sendErrorResponse(getDialogPath().getInvite(), getDialogPath().getLocalTag(), code);

    // Remove the session in the session manager
    imsService.removeSession(this);
  }

  /** Accept the session invitation */
  public void acceptSession() {
    if (logger.isActivated()) {
      logger.debug("Session invitation has been accepted");
    }
    invitationStatus = INVITATION_ACCEPTED;

    // Unblock semaphore
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }
  }

  /**
   * Wait session invitation answer
   *
   * @return Answer
   */
  public int waitInvitationAnswer() {
    if (invitationStatus != INVITATION_NOT_ANSWERED) {
      return invitationStatus;
    }

    if (logger.isActivated()) {
      logger.info("Wait session invitation answer");
    }

    // Wait until received response or received timeout
    try {
      synchronized (waitUserAnswer) {
        waitUserAnswer.wait(ringingPeriod * 1000);
      }
    } catch (InterruptedException e) {
      if (logger.isActivated()) {
        logger.info("ABC Wait session invitation answer1");
      }
      sessionInterrupted = true;
    }

    return invitationStatus;
  }

  /** Interrupt session */
  public void interruptSession() {
    if (logger.isActivated()) {
      logger.info("ABC Interrupt the session");
    }

    try {
      // Unblock semaphore
      synchronized (waitUserAnswer) {
        waitUserAnswer.notifyAll();
      }

      if (!isSessionInterrupted()) {
        if (logger.isActivated()) {
          logger.info("ABC Interrupt the session1");
        }
        // Interrupt thread
        sessionInterrupted = true;
        interrupt();
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't interrupt the session correctly", e);
      }
    }
    if (logger.isActivated()) {
      logger.debug("Session has been interrupted");
    }
  }

  /** M: do not send SIP BYE when timeout to distinguish BOOTED from DEPARTED @{ */
  /** Abort the session */
  public void abortSessionWithoutBye() {
    if (logger.isActivated()) {
      logger.info("abortSessionWithoutBye() entry");
    }

    // Interrupt the session
    interruptSession();

    // Terminate session
    terminateSessionWithoutBy();

    // Close media session
    closeMediaSession();

    // Remove the current session
    getImsService().removeSession(this);

    // Notify listeners
    int size = getListeners().size();
    for (int i = 0; i < size; i++) {
      getListeners().get(i).handleSessionAborted(0);
    }
  }

  /** @} */

  /**
   * Abort the session
   *
   * @param reason Termination reason
   */
  public void abortSession(int reason) {
    if (logger.isActivated()) {
      logger.info("ABC Abort the session " + reason);
      if ((netSwitchInfo.get_ims_off_by_network())) {
        logger.info("ABC Abort the session Network gone");
      }
    }

    // Interrupt the session
    interruptSession();
    if (!(netSwitchInfo.get_ims_off_by_network()) || !(this instanceof HttpFileTransferSession)) {
      // Terminate session
      terminateSession(reason);

      if (logger.isActivated()) {
        logger.info("ABC Abort the session " + reason);
      }

      // Close media session
      closeMediaSession();

      // Remove the current session
      getImsService().removeSession(this);

      // Notify listeners
      for (int i = 0; i < getListeners().size(); i++) {
        getListeners().get(i).handleSessionAborted(reason);
      }
      if (netSwitchInfo.get_ims_off_by_network()) {
        netSwitchInfo.reset_ims_off_by_network();
      }
    }
    if ((this instanceof HttpFileTransferSession)) {
      ((HttpFileTransferSession) this).pauseFileTransfer();
    }
  }

  /** M: do not send SIP BYE when timeout to distinguish BOOTED from DEPARTED @{ */
  /** Terminate session */
  public void terminateSessionWithoutBy() {
    if (dialogPath.isSessionTerminated()) {
      // Already terminated
      return;
    }

    // Stop session timer
    getSessionTimerManager().stop();

    // Update dialog path
    dialogPath.sessionTerminated();

    // Unblock semaphore (used for terminating side only)
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }
  }

  /**
   * Terminate session
   *
   * @param reason Reason
   */
  public void terminateSession(int reason) {
    if (logger.isActivated()) {
      logger.debug("Terminate the session (reason " + reason + ")");
    }

    if ((dialogPath == null) || dialogPath.isSessionTerminated()) {
      // Already terminated
      return;
    }

    // Stop session timer
    getSessionTimerManager().stop();

    // Update dialog path
    if (reason == ImsServiceSession.TERMINATION_BY_USER) {
      dialogPath.sessionTerminated(200, "Call completed");
    } else {
      dialogPath.sessionTerminated();
    }

    // Unblock semaphore (used for terminating side only)
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }

    try {
      // Terminate the session
      if (dialogPath.isSigEstablished()) {
        // Increment the Cseq number of the dialog path
        getDialogPath().incrementCseq();

        // Send BYE without waiting a response
        getImsService().getImsModule().getSipManager().sendSipBye(getDialogPath());
      } else {
        // Send CANCEL without waiting a response
        getImsService().getImsModule().getSipManager().sendSipCancel(getDialogPath());
      }

      if (logger.isActivated()) {
        logger.debug("SIP session has been terminated");
      }
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session termination has failed", e);
      }
    }

    if (this.getDialogPath().isSigEstablished()
        && reason != ImsServiceSession.TERMINATION_BY_USER) {
      for (int j = 0; j < getListeners().size(); j++) {
        final ImsSessionListener listener = getListeners().get(j);
        if (listener instanceof FileSharingSessionListener) {
          AsyncTask.execute(
              new Runnable() {
                @Override
                public void run() {
                  try {
                    Thread.sleep(3 * 1000);
                  } catch (InterruptedException e) {
                    // Nothing to do
                  }
                  ((FileSharingSessionListener) listener)
                      .handleTransferError(
                          new FileSharingError(FileSharingError.MEDIA_TRANSFER_FAILED));
                }
              });
        }
      }
    }
  }

  /**
   * Receive BYE request
   *
   * @param bye BYE request
   */
  public void receiveBye(SipRequest bye) {
    if (logger.isActivated()) {
      logger.info("ABC Receive a BYE message from the remote");
    }

    // Close media session
    closeMediaSession();

    // Update the dialog path status
    getDialogPath().sessionTerminated();
    sessionTerminatedByRemote = true;

    // Remove the current session
    getImsService().removeSession(this);

    // Stop session timer
    getSessionTimerManager().stop();

    // Notify listeners
    for (int i = 0; i < getListeners().size(); i++) {
      getListeners().get(i).handleSessionTerminatedByRemote();
    }
  }

  /**
   * Receive CANCEL request
   *
   * @param cancel CANCEL request
   */
  public void receiveCancel(SipRequest cancel) {
    if (logger.isActivated()) {
      logger.info("ABC Receive a CANCEL message from the remote");
    }

    if (getDialogPath().isSigEstablished()) {
      if (logger.isActivated()) {
        logger.info(
            "Ignore the received CANCEL message from the remote (session already established)");
      }
      return;
    }

    // Close media session
    closeMediaSession();

    // Update dialog path
    getDialogPath().sessionCancelled();

    // Send a 487 Request terminated
    try {
      if (logger.isActivated()) {
        logger.info("Send 487 Request terminated");
      }
      SipResponse terminatedResp =
          SipMessageFactory.createResponse(
              getDialogPath().getInvite(), getDialogPath().getLocalTag(), 487);
      getImsService().getImsModule().getSipManager().sendSipResponse(terminatedResp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 487 error response", e);
      }
    }

    // Remove the current session
    getImsService().removeSession(this);

    // Set invitation status
    invitationStatus = ImsServiceSession.INVITATION_CANCELED;

    // Unblock semaphore
    synchronized (waitUserAnswer) {
      waitUserAnswer.notifyAll();
    }

    // Notify listeners
    for (int i = 0; i < getListeners().size(); i++) {
      getListeners().get(i).handleSessionTerminatedByRemote();
    }

    // Request capabilities to the remote
    getImsService()
        .getImsModule()
        .getCapabilityService()
        .requestContactCapabilities(getDialogPath().getRemoteParty());
  }

  /**
   * Receive re-INVITE request
   *
   * @param reInvite re-INVITE request
   */
  public void receiveReInvite(SipRequest reInvite) {
    // Session refresh management
    sessionTimer.receiveReInvite(reInvite);
  }

  /**
   * Receive UPDATE request
   *
   * @param update UPDATE request
   */
  public void receiveUpdate(SipRequest update) {
    sessionTimer.receiveUpdate(update);
  }

  /**
   * Prepare media session
   *
   * @throws Exception
   */
  public abstract void prepareMediaSession() throws Exception;

  // public abstract void setMsrpFtSupport(boolean ftSupport);

  /**
   * Start media session
   *
   * @throws Exception
   */
  public abstract void startMediaSession() throws Exception;

  /** Close media session */
  public abstract void closeMediaSession();

  /**
   * Send a 180 Ringing response to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   */
  public void send180Ringing(SipRequest request, String localTag) {
    try {
      SipResponse progress = SipMessageFactory.createResponse(request, localTag, 180);
      getImsService().getImsModule().getSipManager().sendSipResponse(progress);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send a 180 Ringing response");
      }
    }
  }

  /**
   * Send an error response to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   * @param code Response code
   */
  public void sendErrorResponse(SipRequest request, String localTag, int code) {
    try {
      // Send  error
      if (logger.isActivated()) {
        logger.info("Send " + code + " error response");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, localTag, code);
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send error response", e);
      }
    }
  }

  /**
   * Send a 603 "Decline" to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   */
  public void send603Decline(SipRequest request, String localTag) {
    try {
      // Send a 603 Decline error
      if (logger.isActivated()) {
        logger.info("Send 603 Decline");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, localTag, 603);
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 603 Decline response", e);
      }
    }
  }

  /**
   * Send a 486 "Busy" to the remote party
   *
   * @param request SIP request
   * @param localTag Local tag
   */
  public void send486Busy(SipRequest request, String localTag) {
    try {
      // Send a 486 Busy error
      if (logger.isActivated()) {
        logger.info("Send 486 Busy");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, localTag, 486);
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 486 Busy response", e);
      }
    }
  }

  /**
   * Send a 415 "Unsupported Media Type" to the remote party
   *
   * @param request SIP request
   */
  public void send415Error(SipRequest request) {
    try {
      if (logger.isActivated()) {
        logger.info("Send 415 Unsupported Media Type");
      }
      SipResponse resp = SipMessageFactory.createResponse(request, 415);
      // TODO: set Accept-Encoding header
      getImsService().getImsModule().getSipManager().sendSipResponse(resp);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Can't send 415 error response", e);
      }
    }
  }

  /**
   * Create SDP setup offer (see RFC6135, RFC4145)
   *
   * @return Setup offer
   */
  public String createSetupOffer() {
    if (isBehindNat()) {
      // Active mode by default if there is a NAT
      return "active";
    } else {
      // Active/passive mode is exchanged in order to be compatible
      // with UE not supporting COMEDIA
      return "actpass";
    }
  }

  /**
   * Create SDP setup offer for mobile to mobile (see RFC6135, RFC4145)
   *
   * @return Setup offer
   */
  public String createMobileToMobileSetupOffer() {
    // Always active mode proposed here
    return "active";
  }

  /**
   * Create SDP setup answer (see RFC6135, RFC4145)
   *
   * @param offer setup offer
   * @return Setup answer ("active" or "passive")
   */
  public String createSetupAnswer(String offer) {
    if (offer.equals("actpass")) {
      // Active mode by default if there is a NAT or AS IM
      return "active";
    } else if (offer.equals("active")) {
      // Passive mode
      return "passive";
    } else if (offer.equals("passive")) {
      // Active mode
      return "active";
    } else {
      // Passive mode by default
      return "passive";
    }
  }

  /**
   * Returns the response timeout (in seconds)
   *
   * @return Timeout
   */
  public int getResponseTimeout() {
    return ringingPeriod + SipManager.TIMEOUT;
  }

  /**
   * Is session interrupted
   *
   * @return Boolean
   */
  public boolean isSessionInterrupted() {
    if (sessionInterrupted) {
      if (logger.isActivated()) {
        logger.info("ABC isSessionInterrupted1");
      }
    }
    if (isInterrupted()) {
      if (logger.isActivated()) {
        logger.info("ABC isSessionInterrupted2");
      }
    }
    if (getDialogPath() != null && getDialogPath().isSessionTerminated()) {
      if (logger.isActivated()) {
        logger.info("ABC isSessionInterrupted3");
      }
    }
    return sessionInterrupted
        || isInterrupted()
        || (getDialogPath() != null && getDialogPath().isSessionTerminated());
  }

  /**
   * Is session terminated by remote
   *
   * @return Boolean
   */
  public boolean isSessionTerminatedByRemote() {
    return sessionTerminatedByRemote;
  }

  /**
   * Create an INVITE request
   *
   * @return the INVITE request
   * @throws SipException
   */
  public abstract SipRequest createInvite() throws SipException;

  /**
   * Send INVITE message
   *
   * @param invite SIP INVITE
   * @throws SipException
   */
  public void sendInvite(SipRequest invite) throws SipException {
    // Send INVITE request
    SipTransactionContext ctx =
        getImsService()
            .getImsModule()
            .getSipManager()
            .sendSipMessageAndWait(invite, getResponseTimeout());

    // Analyze the received response
    if (ctx.isSipResponse()) {
      // A response has been received
      if (ctx.getStatusCode() == 200) {
        // 200 OK
        handle200OK(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 404) {
        // 404 session not found
        handle404SessionNotFound(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 407) {
        // 407 Proxy Authentication Required
        handle407Authentication(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 422) {
        // 422 Session Interval Too Small
        handle422SessionTooSmall(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 480) {
        // 480 Temporarily Unavailable
        handle480Unavailable(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 486) {
        // 486 busy
        handle486Busy(ctx.getSipResponse());
      } else if (ctx.getStatusCode() == 487) {
        // 487 Invitation cancelled
        handle487Cancel(ctx.getSipResponse());
      } else {
        if (ctx.getStatusCode() == 603) {
          // 603 Invitation declined
          handle603Declined(ctx.getSipResponse());
        } else
          // Other error response
          handleDefaultError(ctx.getSipResponse());
      }
    } else {
      // No response received: timeout
      handleError(
          new ImsSessionBasedServiceError(
              ImsSessionBasedServiceError.SESSION_INITIATION_FAILED, "timeout"));
    }
  }

  /**
   * Handle 200 0K response
   *
   * @param resp 200 OK response
   */
  public void handle200OK(SipResponse resp) {
    try {
      // 200 OK received
      if (logger.isActivated()) {
        logger.info("200 OK response received");
      }

      // The signaling is established
      getDialogPath().sigEstablished();

      // Set the remote tag
      getDialogPath().setRemoteTag(resp.getToTag());

      // Set the target
      getDialogPath().setTarget(resp.getContactURI());

      // Set the route path with the Record-Route header
      Vector<String> newRoute = SipUtils.routeProcessing(resp, true);
      getDialogPath().setRoute(newRoute);

      // Set the remote SDP part
      getDialogPath().setRemoteContent(resp.getContent());

      Capabilities capabilities = CapabilityUtils.extractCapabilities(resp);
      if (capabilities != null && this instanceof GroupChatSession) {
        ((GroupChatSession) this).setMsrpFtSupport(capabilities.isFileTransferSupported());
      }

      // Set the remote SIP instance ID
      ContactHeader remoteContactHeader = (ContactHeader) resp.getHeader(ContactHeader.NAME);
      if (remoteContactHeader != null) {
        getDialogPath()
            .setRemoteSipInstance(remoteContactHeader.getParameter(SipUtils.SIP_INSTANCE_PARAM));
      }

      // Prepare Media Session
      prepareMediaSession();

      // Send ACK request
      if (logger.isActivated()) {
        logger.info("Send ACK");
      }
      getImsService().getImsModule().getSipManager().sendSipAck(getDialogPath());

      // The session is established
      getDialogPath().sessionEstablished();

      // Start Media Session
      startMediaSession();

      // Notify listeners
      for (int i = 0; i < getListeners().size(); i++) {
        getListeners().get(i).handleSessionStarted();
      }

      // Start session timer
      if (getSessionTimerManager().isSessionTimerActivated(resp)) {
        getSessionTimerManager()
            .start(resp.getSessionTimerRefresher(), resp.getSessionTimerExpire());
      }
    } catch (Exception e) {
      // Unexpected error
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }
      handleError(new ImsServiceError(ImsServiceError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  /**
   * Handle default error
   *
   * @param resp Error response
   */
  public void handleDefaultError(SipResponse resp) {
    // Default handle
    handleError(
        new ImsSessionBasedServiceError(
            ImsSessionBasedServiceError.SESSION_INITIATION_FAILED,
            resp.getStatusCode() + " " + resp.getReasonPhrase()));
  }

  /** M: Added to resolve the rich call 403 error.@{ */
  /**
   * Handle 403 error. First do re-register then send request again
   *
   * @param request The request was responded with 403
   */
  public void handle403Forbidden(SipResponse resp) {
    if (logger.isActivated()) {
      logger.debug("handle403Forbidden() entry");
    }
    boolean isRegistered =
        imsService
            .getImsModule()
            .getCurrentNetworkInterface()
            .getRegistrationManager()
            .registration();
    if (logger.isActivated()) {
      logger.debug("re-register isRegistered: " + isRegistered);
    }
    if (isRegistered) {
      String callId = dialogPath.getCallId();
      SipRequest invite = createSipInvite(callId);
      if (invite != null) {
        try {
          sendInvite(invite);
        } catch (SipException e) {
          if (logger.isActivated()) {
            logger.debug("re send sip request failed.");
          }
          e.printStackTrace();
        }

      } else {
        if (logger.isActivated()) {
          logger.debug("handle403Forbidden() invite is null");
        }
      }
    }
    if (logger.isActivated()) {
      logger.debug("handle403Forbidden() exit");
    }
    handleDefaultError(resp);
  }

  /**
   * Handle 403 Forbidden
   *
   * @param resp 403 response
   */
  // public void handle403Forbidden(SipResponse resp) {
  //    handleDefaultError(resp);
  // }

  /**
   * Handle 404 Session Not Found
   *
   * @param resp 404 response
   */
  public void handle404SessionNotFound(SipResponse resp) {
    handleDefaultError(resp);
  }

  /**
   * Handle 407 Proxy Authentication Required
   *
   * @param resp 407 response
   */
  public void handle407Authentication(SipResponse resp) {
    try {
      if (logger.isActivated()) {
        logger.info("407 response received");
      }

      // Set the remote tag
      getDialogPath().setRemoteTag(resp.getToTag());

      // Update the authentication agent
      getAuthenticationAgent().readProxyAuthenticateHeader(resp);

      // Increment the Cseq number of the dialog path
      getDialogPath().incrementCseq();

      // Create the invite request
      SipRequest invite = createInvite();

      // Reset initial request in the dialog path
      getDialogPath().setInvite(invite);

      // Set the Proxy-Authorization header
      getAuthenticationAgent().setProxyAuthorizationHeader(invite);

      // Send INVITE request
      sendInvite(invite);

    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(new ImsServiceError(ImsServiceError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  /**
   * Handle 422 response
   *
   * @param resp 422 response
   */
  public void handle422SessionTooSmall(SipResponse resp) {
    try {
      // 422 response received
      if (logger.isActivated()) {
        logger.info("422 response received");
      }

      // Extract the Min-SE value
      int minExpire = SipUtils.getMinSessionExpirePeriod(resp);
      if (minExpire == -1) {
        if (logger.isActivated()) {
          logger.error("Can't read the Min-SE value");
        }
        handleError(
            new ImsSessionBasedServiceError(
                ImsSessionBasedServiceError.UNEXPECTED_EXCEPTION, "No Min-SE value found"));
        return;
      }

      // Set the min expire value
      getDialogPath().setMinSessionExpireTime(minExpire);

      // Set the expire value
      getDialogPath().setSessionExpireTime(minExpire);

      // Increment the Cseq number of the dialog path
      getDialogPath().incrementCseq();

      // Create a new INVITE with the right expire period
      if (logger.isActivated()) {
        logger.info("Send new INVITE");
      }
      SipRequest invite = createInvite();

      // Set the Authorization header
      getAuthenticationAgent().setAuthorizationHeader(invite);

      // Reset initial request in the dialog path
      getDialogPath().setInvite(invite);

      // Send INVITE request
      sendInvite(invite);
    } catch (Exception e) {
      if (logger.isActivated()) {
        logger.error("Session initiation has failed", e);
      }

      // Unexpected error
      handleError(
          new ImsSessionBasedServiceError(
              ImsSessionBasedServiceError.UNEXPECTED_EXCEPTION, e.getMessage()));
    }
  }

  protected SipRequest createSipInvite(String callId) {
    logger.debug("ImsServiceSession::createSipInvite(), do nothing in the parent class");
    return null;
  }

  /**
   * Handle 480 Temporarily Unavailable
   *
   * @param resp 480 response
   */
  public void handle480Unavailable(SipResponse resp) {
    handleDefaultError(resp);
  }

  /**
   * Handle 486 Busy
   *
   * @param resp 486 response
   */
  public void handle486Busy(SipResponse resp) {
    handleDefaultError(resp);
  }

  /**
   * Handle 487 Cancel
   *
   * @param resp 487 response
   */
  public void handle487Cancel(SipResponse resp) {
    handleError(
        new ImsSessionBasedServiceError(
            ImsSessionBasedServiceError.SESSION_INITIATION_CANCELLED,
            resp.getStatusCode() + " " + resp.getReasonPhrase()));
  }

  /**
   * Handle 603 Decline
   *
   * @param resp 603 response
   */
  public void handle603Declined(SipResponse resp) {
    handleError(
        new ImsSessionBasedServiceError(
            ImsSessionBasedServiceError.SESSION_INITIATION_DECLINED,
            resp.getStatusCode() + " " + resp.getReasonPhrase()));
  }

  /**
   * Handle Error
   *
   * @param error ImsServiceError
   */
  public abstract void handleError(ImsServiceError error);

  /**
   * Handle ReInvite Sip Response
   *
   * @param response Sip response to reInvite
   * @param int code response code
   * @param reInvite reInvite SIP request
   */
  public void handleReInviteResponse(int code, SipResponse response, int requestType) {}

  /**
   * Handle User Answer in Response to Session Update notification
   *
   * @param int code response code
   * @param reInvite reInvite SIP request
   */
  public void handleReInviteUserAnswer(int code, int requestType) {}

  /**
   * Handle ACK sent in Response to 200Ok ReInvite
   *
   * @param int code response code
   * @param reInvite reInvite SIP request
   */
  public void handleReInviteAck(int code, int requestType) {}

  /**
   * Handle 407 Proxy Authent error ReInvite Response
   *
   * @param response reInvite SIP response
   * @param int requestType service context of reInvite
   */
  public void handleReInvite407ProxyAuthent(SipResponse response, int serviceContext) {}

  public String buildReInviteSdpResponse(SipRequest ReInvite, int serviceContext) {
    return null;
  }
}