/**
   * 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 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);
      }
    }
  }
  /**
   * 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);
  }
  /**
   * 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);
      }
    }
  }
  /**
   * 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);
      }
    }
  }
  /** 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();
    }
  }