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