# HG changeset patch # User Nikita Kozlov # Date 1293292860 -3600 # Node ID 0e5b95573614f6f4a639cf3fea5fe005f76cf637 # Parent 696b2880c994b516a3ac5dfc1de4f80802500d58# Parent 532fdf132146e972b456caafbcdddc8151910a3f merge diff -r 696b2880c994 -r 0e5b95573614 AndroidManifest.xml --- a/AndroidManifest.xml Tue Dec 21 23:50:59 2010 +0100 +++ b/AndroidManifest.xml Sat Dec 25 17:01:00 2010 +0100 @@ -69,6 +69,12 @@ android:name="com.beem.project.beem.service.XmppConnectionAdapter.CONNECTION_CLOSED" /> + + + + + - - diff -r 696b2880c994 -r 0e5b95573614 res/layout/ongoing_call_notification.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/res/layout/ongoing_call_notification.xml Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + diff -r 696b2880c994 -r 0e5b95573614 res/menu/call.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/res/menu/call.xml Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,10 @@ + + + + + + diff -r 696b2880c994 -r 0e5b95573614 res/menu/contactlist_context.xml --- a/res/menu/contactlist_context.xml Tue Dec 21 23:50:59 2010 +0100 +++ b/res/menu/contactlist_context.xml Sat Dec 25 17:01:00 2010 +0100 @@ -5,7 +5,7 @@ + android:title="@string/CDCall" android:visible="true" /> diff -r 696b2880c994 -r 0e5b95573614 res/raw/ringback.ogg Binary file res/raw/ringback.ogg has changed diff -r 696b2880c994 -r 0e5b95573614 res/values-fr/strings.xml --- a/res/values-fr/strings.xml Tue Dec 21 23:50:59 2010 +0100 +++ b/res/values-fr/strings.xml Sat Dec 25 17:01:00 2010 +0100 @@ -150,6 +150,7 @@ Beem - Contacts Beem - ContactList Activity Beem - Informations + Beem - Appel Create this account diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/BeemService.java --- a/src/com/beem/project/beem/BeemService.java Tue Dec 21 23:50:59 2010 +0100 +++ b/src/com/beem/project/beem/BeemService.java Sat Dec 25 17:01:00 2010 +0100 @@ -78,6 +78,7 @@ import android.preference.PreferenceManager; import android.util.Log; +import com.beem.project.beem.jingle.JingleService; import com.beem.project.beem.service.XmppConnectionAdapter; import com.beem.project.beem.service.XmppFacade; import com.beem.project.beem.service.aidl.IXmppFacade; @@ -106,6 +107,7 @@ private NotificationManager mNotificationManager; private XmppConnectionAdapter mConnection; + private JingleService mJingle; private SharedPreferences mSettings; private String mLogin; private String mPassword; @@ -219,7 +221,8 @@ mConnection = new XmppConnectionAdapter(mConnectionConfiguration, mLogin, mPassword, this); Roster.setDefaultSubscriptionMode(SubscriptionMode.manual); - mBind = new XmppFacade(mConnection); + mJingle = new JingleService(mConnection.getAdaptee(), this); + mBind = new XmppFacade(mConnection, mJingle); Log.d(TAG, "ONCREATE"); } @@ -290,6 +293,7 @@ * @param adaptee XmppConnection used for jingle. */ public void initJingle(XMPPConnection adaptee) { + mJingle.initWhenConntected(adaptee); } /** diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/jingle/JingleService.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/jingle/JingleService.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,310 @@ +/* + BEEM is a videoconference application on the Android Platform. + + Copyright (C) 2009 by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + This file is part of BEEM. + + BEEM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BEEM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BEEM. If not, see . + + Please send bug reports with examples or suggestions to + contact@beem-project.com or http://dev.beem-project.com/ + + Epitech, hereby disclaims all copyright interest in the program "Beem" + written by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + Nicolas Sadirac, November 26, 2009 + President of Epitech. + + Flavien Astraud, November 26, 2009 + Head of the EIP Laboratory. + + */ +package com.beem.project.beem.jingle; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.jingle.JingleManager; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.JingleSessionRequest; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener; +import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.BasicTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +import android.content.Context; +import android.net.Uri; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.util.Log; +import android.content.Intent; + +import com.beem.project.beem.ui.Call; +import com.beem.project.beem.jingle.RTPAudioSession; +import com.beem.project.beem.service.aidl.IBeemJingleListener; +import com.beem.project.beem.service.aidl.IJingle; + +/** + * Beem Jingle Service, manage jingle call. + * @author nikita + */ +public class JingleService extends IJingle.Stub { + private static final String TAG = "JingleService"; + private JingleManager mJingleManager; + private final List mMediaManagers; + private final RemoteCallbackList mRemoteJingleListeners = new RemoteCallbackList(); + private JingleSession mIn; + + private JingleSession mOut; + private JingleSessionRequest mRequest; + private Context mContext; + private boolean isCaller; + private RTPAudioSession mAudioSession; + + /** + * JingleService constructor. + * @param xmppConnection a valid XMPPConnection + */ + public JingleService(final XMPPConnection xmppConnection,final Context ctx) { + BasicTransportManager bt = new BasicTransportManager(); + mMediaManagers = new ArrayList(); + mMediaManagers.add(new MicrophoneRTPManager(bt, ctx)); + mContext = ctx; + } + + /** + * finish to construct the instance. + * @param conn the xmppConnection used with constructor + */ + public void initWhenConntected(XMPPConnection conn) { + mJingleManager = new JingleManager(conn, mMediaManagers); + mJingleManager.addJingleSessionRequestListener(new BeemJingleSessionRequestListener()); + } + + /** + * {@inheritDoc} + */ + @Override + public void addJingleListener(IBeemJingleListener listen) throws RemoteException { + if (listen != null) + mRemoteJingleListeners.register(listen); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeJingleListener(IBeemJingleListener listen) throws RemoteException { + if (listen != null) + mRemoteJingleListeners.unregister(listen); + } + + /** + * begin a jingle call. + * @param receiver the call receiver + */ + @Override + public void call(final String receiver) throws RemoteException { + try { + mOut = mJingleManager.createOutgoingJingleSession(receiver); + mOut.addListener(new BeemJingleSessionListener()); + mOut.startOutgoing(); + isCaller = true; + } catch (XMPPException e) { + e.printStackTrace(); + } + } + + + @Override + public void acceptCall() throws RemoteException { + try { + mRequest.accept(); + mIn.start(); + } catch (XMPPException e) { + e.printStackTrace(); + } + isCaller = false; + } + + @Override + public void setSpeakerMode(int mode) throws RemoteException { + mAudioSession.setSpeakerMode(mode); + } + + /** + * close a jingle call. + */ + @Override + public void closeCall() throws RemoteException { + mAudioSession = null; + if (isCaller) { + try { + mOut.terminate("Cancelled"); + mOut.close(); + } catch (XMPPException e) { + e.printStackTrace(); + } + mOut = null; + } else { + try { + mRequest.reject(); + mIn.terminate(); + mIn.close(); + } catch (XMPPException e) { + e.printStackTrace(); + } + mIn = null; + } + } + + /** + * Listen on session events. + * @author nikita + */ + private class BeemJingleSessionListener implements JingleSessionListener { + + /** + * constructor. + */ + public BeemJingleSessionListener() { + super(); + } + + @Override + public void sessionClosed(String reason, JingleSession jingleSession) { + System.out.println("Session " + jingleSession.getResponder() + "closedd because " + reason); + + final int n = mRemoteJingleListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IBeemJingleListener listener = mRemoteJingleListeners.getBroadcastItem(i); + try { + listener.sessionClosed(reason); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + mRemoteJingleListeners.finishBroadcast(); + } + + @Override + public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) { + System.out.println("Session " + jingleSession.getResponder() + " closed"); + + final int n = mRemoteJingleListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IBeemJingleListener listener = mRemoteJingleListeners.getBroadcastItem(i); + try { + listener.sessionClosedOnError(e.getMessage()); + } catch (RemoteException err) { + err.printStackTrace(); + } + } + mRemoteJingleListeners.finishBroadcast(); + } + + @Override + public void sessionDeclined(String reason, JingleSession jingleSession) { + Log.d(TAG, "Session " + jingleSession.getResponder() + "declined because " + reason); + + final int n = mRemoteJingleListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IBeemJingleListener listener = mRemoteJingleListeners.getBroadcastItem(i); + try { + listener.sessionDeclined(reason); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + mRemoteJingleListeners.finishBroadcast(); + + } + + @Override + public void sessionEstablished(PayloadType pt, TransportCandidate remoteCandidate, + TransportCandidate localCandidate, JingleSession jingleSession) { + Log.d(TAG, "Session " + jingleSession.getResponder() + "established"); + mAudioSession = (RTPAudioSession) jingleSession.getSession().getMediaSession(MicrophoneRTPManager.MEDIA_NAME); + final int n = mRemoteJingleListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IBeemJingleListener listener = mRemoteJingleListeners.getBroadcastItem(i); + try { + listener.sessionEstablished(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + mRemoteJingleListeners.finishBroadcast(); + } + + @Override + public void sessionMediaReceived(JingleSession jingleSession, String participant) { + Log.d(TAG, "Session Media received from " + participant); + } + + @Override + public void sessionRedirected(String redirection, JingleSession jingleSession) { + } + } + + + /** + * Listen for a Jingle session request. + * @author nikita + */ + private class BeemJingleSessionRequestListener implements JingleSessionRequestListener { + + /** + * Constructor. + */ + public BeemJingleSessionRequestListener() { + super(); + } + + @Override + public void sessionRequested(JingleSessionRequest request) { + mRequest = request; + try { + mIn = mJingleManager.createIncomingJingleSession(mRequest); + mIn.addListener(new BeemJingleSessionListener()); + mIn.startIncoming(); + } catch (XMPPException e) { + e.printStackTrace(); + } + System.out.println("Jingle Session request from " + request.getFrom()); + isCaller = false; + Intent intent = new Intent(mContext, Call.class); + intent.setData(Uri.parse("jingle:"+request.getFrom())); + intent.putExtra("isCaller", false); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + } + +} diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/jingle/MicrophoneRTPManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/jingle/MicrophoneRTPManager.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,116 @@ +/* + BEEM is a videoconference application on the Android Platform. + + Copyright (C) 2009 by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + This file is part of BEEM. + + BEEM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BEEM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BEEM. If not, see . + + Please send bug reports with examples or suggestions to + contact@beem-project.com or http://dev.beem-project.com/ + + Epitech, hereby disclaims all copyright interest in the program "Beem" + written by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + Nicolas Sadirac, November 26, 2009 + President of Epitech. + + Flavien Astraud, November 26, 2009 + Head of the EIP Laboratory. + +*/ +package com.beem.project.beem.jingle; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; + +import android.content.Context; + +/** + * RTPMediaManager, gere les payloads et renvoie une session RTP. + * @author nikita + */ +public class MicrophoneRTPManager extends JingleMediaManager { + /** + * RTP media name. + */ + public static final String MEDIA_NAME = "Microphone"; + + private List mPayloads; + + private Context mCtx; + + /** + * Manage Microphone data transmission trough RTP. + * @param transportManager current jingle transport manager(basic,upnp,ice...). + */ + public MicrophoneRTPManager(final JingleTransportManager transportManager, Context ctx) { + super(transportManager); + setupPayloads(); + mCtx = ctx; + } + + /* (non-Javadoc) + * @see org.jivesoftware.smackx.jingle.media.JingleMediaManager#createMediaSession(org.jivesoftware.smackx.jingle.media.PayloadType, org.jivesoftware.smackx.jingle.nat.TransportCandidate, org.jivesoftware.smackx.jingle.nat.TransportCandidate, org.jivesoftware.smackx.jingle.JingleSession) + */ + @Override + public JingleMediaSession createMediaSession(PayloadType payloadType, TransportCandidate remote, + TransportCandidate local, JingleSession jingleSession) { + return new RTPAudioSession(payloadType, remote, local, null, jingleSession, mCtx); + } + + /* (non-Javadoc) + * @see org.jivesoftware.smackx.jingle.media.JingleMediaManager#getName() + */ + @Override + public String getName() { + return MEDIA_NAME; + } + + /* (non-Javadoc) + * @see org.jivesoftware.smackx.jingle.media.JingleMediaManager#getPayloads() + */ + @Override + public List getPayloads() { + return mPayloads; + } + + /** + * Supported payload list. + */ + private void setupPayloads() { + mPayloads = new ArrayList(); + mPayloads.add(new PayloadType.Audio(8, "PCMA")); +// mPayloads.add(new PayloadType.Audio(9, "G722")); +// mPayloads.add(new PayloadType.Audio(3, "GSM")); + } +} diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/jingle/RTPAudioSession.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/jingle/RTPAudioSession.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,89 @@ +package com.beem.project.beem.jingle; + +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.media.JingleMediaSession; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.TransportCandidate; +import org.sipdroid.media.RtpStreamReceiver; +import org.sipdroid.media.RtpStreamSender; +import org.sipdroid.net.SipdroidSocket; + +import android.content.Context; +import android.util.Log; + +/** + * Manage microphone RTP session. + * @author nikita + */ +public class RTPAudioSession extends JingleMediaSession { + + private RtpStreamSender mSender=null; + private RtpStreamReceiver mReceiver=null; + + /** + * constructor. + * @param payloadType the payload typ used + * @param remote the remote transport info + * @param local the local tranport info + * @param mediaLocator don't know + * @param jingleSession the current jingle session + * @param ctx + */ + public RTPAudioSession(final PayloadType pt, final TransportCandidate remote, + final TransportCandidate local, final String mediaLocator, final JingleSession jingleSession, Context ctx) { + super(pt, remote, local, mediaLocator, jingleSession); + Log.d("AUDIO", String.format("payload type : %s ipdest %s port dest %d port src %d",pt.getName(), remote.getIp(), remote.getPort(), local.getPort())); + + SipdroidSocket rtpSocket = null; + try { + rtpSocket = new SipdroidSocket(local.getPort()); + } catch (Exception e) { + e.printStackTrace(); + } + mSender = new RtpStreamSender(true, 8, 8000, 160, rtpSocket, remote.getIp(), remote.getPort()); + mReceiver = new RtpStreamReceiver(rtpSocket, 8); + } + + @Override + public void initialize() { + } + + public void setSpeakerMode(final int mode) { + mReceiver.speaker(mode); + } + + @Override + public void setTrasmit(boolean active) { + } + + @Override + public void startReceive() { + if (mReceiver != null) { + mReceiver.start(); + } + } + + @Override + public void startTrasmit() { + if (mSender != null) { + mSender.start(); + } + } + + @Override + public void stopReceive() { + if (mReceiver != null) { + mReceiver.halt(); + mReceiver = null; + } + + } + + @Override + public void stopTrasmit() { + if (mSender != null) { + mSender.halt(); + mSender = null; + } + } +} diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/service/Contact.java diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/service/RosterAdapter.java diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/service/XmppFacade.java --- a/src/com/beem/project/beem/service/XmppFacade.java Tue Dec 21 23:50:59 2010 +0100 +++ b/src/com/beem/project/beem/service/XmppFacade.java Sat Dec 25 17:01:00 2010 +0100 @@ -47,11 +47,13 @@ import android.os.RemoteException; +import com.beem.project.beem.jingle.JingleService; import com.beem.project.beem.service.aidl.IChatManager; import com.beem.project.beem.service.aidl.IPrivacyListManager; import com.beem.project.beem.service.aidl.IRoster; import com.beem.project.beem.service.aidl.IXmppConnection; import com.beem.project.beem.service.aidl.IXmppFacade; +import com.beem.project.beem.service.aidl.IJingle; import com.beem.project.beem.utils.PresenceType; import com.beem.project.beem.smack.avatar.AvatarManager; @@ -62,13 +64,16 @@ public class XmppFacade extends IXmppFacade.Stub { private final XmppConnectionAdapter mConnexion; + private final JingleService mJingle; /** * Constructor for XMPPFacade. * @param connection the connection use by the facade + * @param jingle the jingle session */ - public XmppFacade(final XmppConnectionAdapter connection) { + public XmppFacade(final XmppConnectionAdapter connection, final JingleService jingle) { this.mConnexion = connection; + this.mJingle = jingle; } /** @@ -118,6 +123,14 @@ public IChatManager getChatManager() throws RemoteException { return mConnexion.getChatManager(); } + + /** + * {@inheritDoc} + */ + @Override + public IJingle getJingleService() throws RemoteException { + return mJingle; + } /** * {@inheritDoc} @@ -147,8 +160,12 @@ */ @Override public void call(String jid) throws RemoteException { + mJingle.call(jid); } + /* (non-Javadoc) + * @see com.beem.project.beem.service.aidl.IXmppFacade#getVcardAvatar(java.lang.String) + */ @Override public byte[] getAvatar(String avatarId) throws RemoteException { AvatarManager mgr = mConnexion.getAvatarManager(); diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/service/aidl/IBeemJingleListener.aidl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/service/aidl/IBeemJingleListener.aidl Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,77 @@ +/* + BEEM is a videoconference application on the Android Platform. + + Copyright (C) 2009 by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + This file is part of BEEM. + + BEEM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BEEM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BEEM. If not, see . + + Please send bug reports with examples or suggestions to + contact@beem-project.com or http://dev.beem-project.com/ + + Epitech, hereby disclaims all copyright interest in the program "Beem" + written by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + Nicolas Sadirac, November 26, 2009 + President of Epitech. + + Flavien Astraud, November 26, 2009 + Head of the EIP Laboratory. + +*/ +package com.beem.project.beem.service.aidl; + +/** + * Interface to listen for jingle sessions events + * @author Nikita Kozlov + */ +interface IBeemJingleListener { + + /** + * Callback to call when the session is closed + */ + void sessionClosed(in String reason); + + /** + * Callback to call when the session is declined + */ + void sessionDeclined(in String reason); + + + /** + * Callback to call when the session is closed on error + */ + void sessionClosedOnError(in String error); + + /** + * Callback to call when session is established + */ + void sessionEstablished(); + + /** + * Callback to call when session is requested + */ + void sessionRequested(in String fromJID); +} diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/service/aidl/IJingle.aidl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/service/aidl/IJingle.aidl Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,75 @@ +/* + BEEM is a videoconference application on the Android Platform. + + Copyright (C) 2009 by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + This file is part of BEEM. + + BEEM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BEEM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BEEM. If not, see . + + Please send bug reports with examples or suggestions to + contact@beem-project.com or http://dev.beem-project.com/ + + Epitech, hereby disclaims all copyright interest in the program "Beem" + written by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + Nicolas Sadirac, November 26, 2009 + President of Epitech. + + Flavien Astraud, November 26, 2009 + Head of the EIP Laboratory. + +*/ +package com.beem.project.beem.service.aidl; + +import com.beem.project.beem.service.aidl.IBeemJingleListener; + + +interface IJingle { + + void addJingleListener(in IBeemJingleListener listen); + void removeJingleListener(in IBeemJingleListener listen); + + /** + * make a jingle audio call + * @param jid the receiver id + */ + void call(in String jid); + + /** + * Accept call a jingle audio call + */ + void acceptCall(); + + /** + * close a jingle audio call + */ + void closeCall(); + + /** + * Set speaker mode + */ + void setSpeakerMode(in int mode); + +} diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/service/aidl/IXmppFacade.aidl --- a/src/com/beem/project/beem/service/aidl/IXmppFacade.aidl Tue Dec 21 23:50:59 2010 +0100 +++ b/src/com/beem/project/beem/service/aidl/IXmppFacade.aidl Sat Dec 25 17:01:00 2010 +0100 @@ -45,9 +45,10 @@ import com.beem.project.beem.service.aidl.IXmppConnection; import com.beem.project.beem.service.aidl.IRoster; +import com.beem.project.beem.service.aidl.IJingle; import com.beem.project.beem.service.aidl.IChatManager; import com.beem.project.beem.service.aidl.IPrivacyListManager; -import com.beem.project.beem.service.PresenceAdapter; +import com.beem.project.beem.service.PresenceAdapter; interface IXmppFacade { @@ -80,6 +81,11 @@ * Get the chat manager. */ IChatManager getChatManager(); + + /** + * Get the Jingle service. + */ + IJingle getJingleService(); /** * Change the status of the user. @@ -90,12 +96,6 @@ void sendPresencePacket(in PresenceAdapter presence); - /** - * make a jingle audio call - * @param jid the receiver id - */ - void call(in String jid); - /** * get the an avatar * @param id the id of the avatar diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/ui/Call.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/ui/Call.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,349 @@ +/* + BEEM is a videoconference application on the Android Platform. + + Copyright (C) 2009 by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + This file is part of BEEM. + + BEEM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BEEM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BEEM. If not, see . + + Please send bug reports with examples or suggestions to + contact@beem-project.com or http://dev.beem-project.com/ + + Epitech, hereby disclaims all copyright interest in the program "Beem" + written by Frederic-Charles Barthelery, + Jean-Manuel Da Silva, + Nikita Kozlov, + Philippe Lago, + Jean Baptiste Vergely, + Vincent Veronis. + + Nicolas Sadirac, November 26, 2009 + President of Epitech. + + Flavien Astraud, November 26, 2009 + Head of the EIP Laboratory. + + */ +package com.beem.project.beem.ui; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.media.AudioManager; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Vibrator; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; +import android.os.RemoteCallbackList; +import android.os.RemoteException; + +import com.beem.project.beem.R; +import com.beem.project.beem.service.aidl.IXmppFacade; +import com.beem.project.beem.service.aidl.IJingle; +import com.beem.project.beem.service.aidl.IBeemJingleListener; + +/** + * This class is an activity which display an animation during the connection with the server. + * @author Da Risk + */ +public class Call extends Activity { + + private static final Intent SERVICE_INTENT = new Intent(); + static { + SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService")); + } + private ImageView mLogo; + private TextView mCallInfo; + private Animation mRotateAnim; + private final ServiceConnection mServConn = new BeemServiceConnection(); + private BeemJingleSessionListener mJingleListener = new BeemJingleSessionListener(); + private IXmppFacade mXmppFacade; + private final Handler mHandler = new Handler(); + private Button mCloseCall; + private Button mAcceptCall; + private IJingle mJingle; + + final static long[] vibratePattern = {0,1000,1000}; + + public static final int UA_STATE_IDLE = 0; + public static final int UA_STATE_INCOMING_CALL = 1; + public static final int UA_STATE_OUTGOING_CALL = 2; + public static final int UA_STATE_INCALL = 3; + public static final int UA_STATE_HOLD = 4; + + public static int call_state = UA_STATE_IDLE; + public static int docked = -1,headset = -1; + public static Ringtone oRingtone; + public static Context mContext; + + public static void stopRingtone() { + android.os.Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); + v.cancel(); + if (oRingtone != null) { + Ringtone ringtone = oRingtone; + oRingtone = null; + ringtone.stop(); + } + } + + public static void startRingtone() { + android.os.Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); + v.vibrate(vibratePattern,1); + oRingtone = RingtoneManager.getRingtone(mContext, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)); + oRingtone.play(); + } + + /** + * Constructor. + */ + public Call() { + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Call.mContext = this; + setContentView(R.layout.call); + Intent callingIntent = this.getIntent(); + + mLogo = (ImageView) findViewById(R.id.call_logo_anim); + mRotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotate_and_scale); + mCloseCall = (Button) findViewById(R.id.call_cancel_button); + mCloseCall.setOnClickListener(new ClickListener()); + if (callingIntent.getBooleanExtra("isCaller", false)) { + mAcceptCall = (Button) findViewById(R.id.call_accept_button); + mAcceptCall.setVisibility(View.GONE); + call_state = UA_STATE_OUTGOING_CALL; + } else { + mAcceptCall = (Button) findViewById(R.id.call_accept_button); + mAcceptCall.setOnClickListener(new ClickListener()); + call_state = UA_STATE_INCOMING_CALL; + Call.startRingtone(); + } + mCallInfo = (TextView) findViewById(R.id.call_info); + } + + @Override + protected void onStart() { + super.onStart(); + mLogo.startAnimation(mRotateAnim); + } + + @Override + protected void onResume() { + super.onResume(); + if (mXmppFacade == null) + bindService(Call.SERVICE_INTENT, mServConn, BIND_AUTO_CREATE); + } + + @Override + protected void onPause() { + super.onPause(); + if (mXmppFacade != null) { + try { + mJingle.removeJingleListener(mJingleListener); + } catch (RemoteException e) { + e.printStackTrace(); + } + unbindService(mServConn); + mXmppFacade = null; + } + } + + @Override + public final boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.call, menu); + return true; + } + + /** + * Callback for menu item selected. + * @param item the item selected + * @return true on success, false otherwise + */ + @Override + public final boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.call_speaker_on: + try { + mJingle.setSpeakerMode(AudioManager.MODE_NORMAL); + } catch (RemoteException e) { + e.printStackTrace(); + } + return true; + case R.id.call_speaker_off: + try { + mJingle.setSpeakerMode(AudioManager.MODE_IN_CALL); + } catch (RemoteException e) { + e.printStackTrace(); + } + return true; + case R.id.call_hold_on: + Call.call_state = UA_STATE_HOLD; + return true; + case R.id.call_hold_off: + Call.call_state = UA_STATE_INCALL; + return true; + default: + return false; + } + } + + /** + * Click event listener on cancel button. + */ + private class ClickListener implements OnClickListener { + + /** + * Constructor. + */ + ClickListener() { + } + + @Override + public void onClick(View v) { + try { + if (v == mCloseCall) { + mJingle.closeCall(); + stopRingtone(); + Call.call_state = UA_STATE_IDLE; + } else if (v == mAcceptCall) { + mJingle.acceptCall(); + stopRingtone(); + Call.call_state = UA_STATE_INCALL; + } + } catch (RemoteException e) { + e.printStackTrace(); + } + //finish(); + } + } + + private class BeemJingleSessionListener extends IBeemJingleListener.Stub { + + /** + * Refresh the call activity. + */ + private class RunnableChange implements Runnable { + + private String mStr; + /** + * Constructor. + */ + public RunnableChange(String str) { + mStr = str; + } + + /** + * {@inheritDoc} + */ + @Override + public void run() { + mCallInfo.setText(mStr); + } + } + + public BeemJingleSessionListener() { + } + + @Override + public void sessionClosed(final String reason) { + android.util.Log.d("TEST", "TEST " + reason); + Call.stopRingtone(); + call_state = UA_STATE_IDLE; + mHandler.post(new RunnableChange(reason)); + } + + @Override + public void sessionDeclined(final String reason) { + android.util.Log.d("TEST", "TEST4 " + reason); + Call.stopRingtone(); + call_state = UA_STATE_IDLE; + mHandler.post(new RunnableChange(reason)); + } + + @Override + public void sessionClosedOnError(final String error) { + android.util.Log.d("TEST", "TEST5 " + error); + Call.stopRingtone(); + call_state = UA_STATE_IDLE; + mHandler.post(new RunnableChange(error)); + } + + @Override + public void sessionEstablished() { + android.util.Log.d("TEST", "TEST2 "); + call_state = UA_STATE_INCALL; + Call.stopRingtone(); + mHandler.post(new RunnableChange("established")); + } + + @Override + public void sessionRequested(final String fromJID) { + android.util.Log.d("TEST", "TEST3 " + fromJID); + mHandler.post(new RunnableChange(fromJID)); + } + } + + /** + * The service connection used to connect to the Beem service. + */ + private class BeemServiceConnection implements ServiceConnection { + + /** + * Constructor. + */ + public BeemServiceConnection() { + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mXmppFacade = IXmppFacade.Stub.asInterface(service); + try { + mJingle = mXmppFacade.getJingleService(); + mJingle.addJingleListener(mJingleListener); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mXmppFacade = null; + } + } +} diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/ui/ContactList.java --- a/src/com/beem/project/beem/ui/ContactList.java Tue Dec 21 23:50:59 2010 +0100 +++ b/src/com/beem/project/beem/ui/ContactList.java Sat Dec 25 17:01:00 2010 +0100 @@ -226,7 +226,11 @@ break; case R.id.contact_list_context_menu_call_item: try { - mXmppFacade.call(mSelectedContact.getJID() + "/psi"); + mJingle.call(mContact.getJID() + "/Beem"); + in = new Intent(this, Call.class); + in.setData(mContact.toUri()); + in.putExtra("isCaller", true); + startActivity(in); result = true; } catch (RemoteException e) { e.printStackTrace(); diff -r 696b2880c994 -r 0e5b95573614 src/com/beem/project/beem/utils/Random.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/beem/project/beem/utils/Random.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005 Luca Veltri - University of Parma - Italy + * + * This file is part of MjSip (http://www.mjsip.org) + * + * MjSip is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MjSip is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MjSip; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author(s): + * Luca Veltri (luca.veltri@unipr.it) + */ + +package com.beem.project.beem.utils; + +/** + * Class Random collects some static methods for generating random numbers and + * other stuff. + */ +public class Random { + /** The random seed */ + static final long seed = System.currentTimeMillis(); + // static final long seed=0; + + static java.util.Random rand = new java.util.Random(seed); + + // static java.util.Random rand=new java.util.Random(); + + /** Returns a random integer between 0 and n-1 */ + /* + * static public int nextInt(int n) { seed=(seed*37)%987654321; return + * (int)(seed%n); } + */ + + /** Returns true or false respectively with probability p/100 and (1-p/100) */ + /* + * static boolean percent(int p) { return integer(100)> 4]; + } + + public static void alaw2linear(byte alaw[],short lin[],int frames) { + int i; + for (i = 0; i < frames; i++) + lin[i] = a2s[alaw[i+12] & 0xff]; + } + + public static void linear2alaw(short lin[],int offset,byte alaw[],int frames) { + int i; + for (i = 0; i < frames; i++) + alaw[i+12] = s2a[lin[i+offset] & 0xffff]; + } + + //change g711 ulaw start + protected static int alaw2ulaw(int aval) + { aval&=0xff; + return ((aval & 0x80)!=0)? (0xFF^_a2u[aval^0xD5]) : (0x7F^_a2u[aval^0x55]); + } + + protected static int ulaw2alaw(int uval) + { uval&=0xff; + return ((uval&0x80)!=0)? (0xD5^(_u2a[0xFF^uval]-1)) : (0x55^(_u2a[0x7F^uval]-1)); + } + + public static void ulaw2linear(byte ulaw[],short lin[],int frames) { + int i; + for (i = 0; i < frames; i++) + lin[i] = a2s[ulaw2alaw(ulaw[i+12] & 0xff)]; + } + public static void linear2ulaw(short lin[],int offset,byte ulaw[],int frames) { + int i; + for (i = 0; i < frames; i++) + ulaw[i+12] = (byte)alaw2ulaw(s2a[lin[i+offset] & 0xffff]); + } + //change end +} diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/media/RtpStreamReceiver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/RtpStreamReceiver.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2005 Luca Veltri - University of Parma - Italy + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.media; + +import java.io.IOException; +import java.net.SocketException; + +import com.beem.project.beem.jingle.JingleService; +import com.beem.project.beem.ui.Call; +import org.sipdroid.net.RtpPacket; +import org.sipdroid.net.RtpSocket; +import org.sipdroid.net.SipdroidSocket; +import org.sipdroid.pjlib.Codec; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences.Editor; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; +import android.media.ToneGenerator; +import android.os.PowerManager; +import android.os.RemoteException; +import android.preference.PreferenceManager; +import android.provider.Settings; + +/** + * RtpStreamReceiver is a generic stream receiver. It receives packets from RTP + * and writes them into an OutputStream. + */ +public class RtpStreamReceiver extends Thread { + + /** Whether working in debug mode. */ + public static boolean DEBUG = true; + + /** Payload type */ + int p_type; + + /** Size of the read buffer */ + public static final int BUFFER_SIZE = 1024; + + /** Maximum blocking time, spent waiting for reading new bytes [milliseconds] */ + public static final int SO_TIMEOUT = 200; + + /** The RtpSocket */ + private RtpSocket rtp_socket = null; + + /** Whether it is running */ + private boolean running; + private AudioManager am; + private ContentResolver cr; + public static int speakermode; + private JingleService mJingle; + + /** + * Constructs a RtpStreamReceiver. + * + * @param output_stream + * the stream sink + * @param socket + * the local receiver SipdroidSocket + */ + public RtpStreamReceiver(SipdroidSocket socket, int payload_type) { + init(socket); + p_type = payload_type; + } + + /** Inits the RtpStreamReceiver */ + private void init(SipdroidSocket socket) { + if (socket != null) + rtp_socket = new RtpSocket(socket); + } + + /** Whether is running */ + public boolean isRunning() { + return running; + } + + /** Stops running */ + public void halt() { + running = false; + } + + public int speaker(int mode) { + int old = speakermode; + + if (Call.headset > 0 && mode == AudioManager.MODE_NORMAL) + return old; + saveVolume(); + setMode(speakermode = mode); + restoreVolume(); + return old; + } + + double smin = 200,s; + public static int nearend; + + void calc(short[] lin,int off,int len) { + int i,j; + double sm = 30000,r; + + for (i = 0; i < len; i += 5) { + j = lin[i+off]; + s = 0.03*Math.abs(j) + 0.97*s; + if (s < sm) sm = s; + if (s > smin) nearend = 3000/5; + else if (nearend > 0) nearend--; + } + for (i = 0; i < len; i++) { + j = lin[i+off]; + if (j > 6550) + lin[i+off] = 6550*5; + else if (j < -6550) + lin[i+off] = -6550*5; + else + lin[i+off] = (short)(j*5); + } + r = (double)len/100000; + smin = sm*r + smin*(1-r); + } + + static void setStreamVolume(final int stream,final int vol,final int flags) { + (new Thread() { + public void run() { + AudioManager am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); + am.setStreamVolume(stream, vol, flags); + if (stream == AudioManager.STREAM_MUSIC) restored = true; + } + }).start(); + } + + static boolean restored; + + public static float getEarGain() { + try { + return Float.valueOf(PreferenceManager.getDefaultSharedPreferences(Call.mContext).getString(Call.headset > 0?"heargain":"eargain", "0.25")); + } catch (NumberFormatException i) { + return (float)0.25; + } + } + + void restoreVolume() { + switch (am.getMode()) { + case AudioManager.MODE_IN_CALL: + setStreamVolume(AudioManager.STREAM_RING,(int)( + am.getStreamMaxVolume(AudioManager.STREAM_RING)* + getEarGain()), 0); + track.setStereoVolume(AudioTrack.getMaxVolume()* + getEarGain() + ,AudioTrack.getMaxVolume()* + getEarGain()); + break; + case AudioManager.MODE_NORMAL: + track.setStereoVolume(AudioTrack.getMaxVolume(),AudioTrack.getMaxVolume()); + break; + } + setStreamVolume(AudioManager.STREAM_MUSIC, + PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("volume"+speakermode, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC)* + (speakermode == AudioManager.MODE_NORMAL?4:3)/4 + ),0); + } + + void saveVolume() { + if (restored) { + Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); + edit.putInt("volume"+speakermode,am.getStreamVolume(AudioManager.STREAM_MUSIC)); + edit.commit(); + } + } + + void saveSettings() { + if (!PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("oldvalid",false)) { + int oldvibrate = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); + int oldvibrate2 = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); + if (!PreferenceManager.getDefaultSharedPreferences(Call.mContext).contains("oldvibrate2")) + oldvibrate2 = AudioManager.VIBRATE_SETTING_ON; + int oldpolicy = android.provider.Settings.System.getInt(cr, android.provider.Settings.System.WIFI_SLEEP_POLICY, + Settings.System.WIFI_SLEEP_POLICY_DEFAULT); + Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); + edit.putInt("oldvibrate", oldvibrate); + edit.putInt("oldvibrate2", oldvibrate2); + edit.putInt("oldpolicy", oldpolicy); + edit.putInt("oldring",am.getStreamVolume(AudioManager.STREAM_RING)); + edit.putBoolean("oldvalid", true); + edit.commit(); + } + } + + public static void setMode(int mode) { + Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); + edit.putBoolean("setmode", mode != AudioManager.MODE_NORMAL); + edit.commit(); + AudioManager am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); + am.setMode(mode); + } + + public static void restoreMode() { + if (PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("setmode",true)) { + setMode(AudioManager.MODE_NORMAL); + } + } + + public static void restoreSettings() { + if (PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("oldvalid",true)) { + AudioManager am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); + ContentResolver cr = Call.mContext.getContentResolver(); + int oldvibrate = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldvibrate",0); + int oldvibrate2 = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldvibrate2",0); + int oldpolicy = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldpolicy",0); + am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,oldvibrate); + am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,oldvibrate2); + Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY, oldpolicy); + setStreamVolume(AudioManager.STREAM_RING, PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldring",0), 0); + Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); + edit.putBoolean("oldvalid", false); + edit.commit(); + PowerManager pm = (PowerManager) Call.mContext.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | + PowerManager.ACQUIRE_CAUSES_WAKEUP, "Sipdroid.RtpStreamReceiver"); + wl.acquire(1000); + } + restoreMode(); + } + + public static float good, late, lost, loss; + public static int timeout; + + void empty() { + try { + rtp_socket.getDatagramSocket().setSoTimeout(1); + for (;;) + rtp_socket.receive(rtp_packet); + } catch (SocketException e2) { + e2.printStackTrace(); + } catch (IOException e) { + } + try { + rtp_socket.getDatagramSocket().setSoTimeout(1000); + } catch (SocketException e2) { + e2.printStackTrace(); + } + } + + RtpPacket rtp_packet; + AudioTrack track; + + /** Runs it in a new Thread. */ + public void run() { + boolean nodata = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("nodata",false); + + if (rtp_socket == null) { + if (DEBUG) + println("ERROR: RTP socket is null"); + return; + } + + byte[] buffer = new byte[BUFFER_SIZE+12]; + byte[] buffer_gsm = new byte[33+12]; + int i; + rtp_packet = new RtpPacket(buffer, 0); + + if (DEBUG) + println("Reading blocks of max " + buffer.length + " bytes"); + + running = true; + speakermode = Call.docked > 0?AudioManager.MODE_NORMAL:AudioManager.MODE_IN_CALL; + restored = false; + + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); + am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); + cr = Call.mContext.getContentResolver(); + saveSettings(); + Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY,Settings.System.WIFI_SLEEP_POLICY_NEVER); + am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,AudioManager.VIBRATE_SETTING_OFF); + am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,AudioManager.VIBRATE_SETTING_OFF); + int oldvol = am.getStreamVolume(AudioManager.STREAM_MUSIC); + track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, + BUFFER_SIZE*2*2, AudioTrack.MODE_STREAM); + short lin[] = new short[BUFFER_SIZE]; + short lin2[] = new short[BUFFER_SIZE]; + int user, server, lserver, luser, cnt, todo, headroom, len = 0, seq = 0, cnt2 = 0, m = 1, + expseq, getseq, vm = 1, gap, gseq; + timeout = 1; + boolean islate; + user = 0; + lserver = 0; + luser = -8000; + cnt = 0; + switch (p_type) { + case 3: + Codec.init(); + break; + case 0: + case 8: + G711.init(); + break; + } + ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_MUSIC,(int)(ToneGenerator.MAX_VOLUME*2*0.95)); + track.play(); + if (Call.headset > 0 && Call.oRingtone != null) { + ToneGenerator tg2 = new ToneGenerator(AudioManager.STREAM_RING,(int)(ToneGenerator.MAX_VOLUME*2*0.95)); + tg2.startTone(ToneGenerator.TONE_SUP_RINGTONE); + System.gc(); + tg2.stopTone(); + } else + System.gc(); + while (running) { + if (Call.call_state == Call.UA_STATE_HOLD) { + tg.stopTone(); + track.pause(); + while (running && Call.call_state == Call.UA_STATE_HOLD) { + try { + sleep(1000); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + track.play(); + System.gc(); + timeout = 1; + seq = 0; + } + try { + rtp_socket.receive(rtp_packet); + if (timeout != 0) { + tg.stopTone(); + track.pause(); + user += track.write(lin2,0,BUFFER_SIZE); + user += track.write(lin2,0,BUFFER_SIZE); + track.play(); + cnt += 2*BUFFER_SIZE; + empty(); + } + timeout = 0; + } catch (IOException e) { + if (timeout == 0 && nodata) { + tg.startTone(ToneGenerator.TONE_SUP_RINGTONE); + } + rtp_socket.getDatagramSocket().disconnect(); + if (++timeout > 22) { + try { + mJingle.closeCall(); + } catch (RemoteException e1) { + e1.printStackTrace(); + } + break; + } + } + if (running && timeout == 0) { + gseq = rtp_packet.getSequenceNumber(); + if (seq == gseq) { + m++; + continue; + } + + server = track.getPlaybackHeadPosition(); + headroom = user-server; + + if (headroom > 1500) + cnt += len; + else + cnt = 0; + + if (lserver == server) + cnt2++; + else + cnt2 = 0; + + if (cnt <= 500 || cnt2 >= 2 || headroom - 875 < len) { + switch (rtp_packet.getPayloadType()) { + case 0: + len = rtp_packet.getPayloadLength(); + G711.ulaw2linear(buffer, lin, len); + break; + case 8: + len = rtp_packet.getPayloadLength(); + G711.alaw2linear(buffer, lin, len); + break; + case 3: + for (i = 12; i < 45; i++) + buffer_gsm[i] = buffer[i]; + len = Codec.decode(buffer_gsm, lin, 0); + break; + } + + if (speakermode == AudioManager.MODE_NORMAL) + calc(lin,0,len); + } + + if (headroom < 250) { + todo = 875 - headroom; + println("insert "+todo); + islate = true; + user += track.write(lin2,0,todo); + } else + islate = false; + + if (cnt > 500 && cnt2 < 2) { + todo = headroom - 875; + println("cut "+todo); + if (todo < len) + user += track.write(lin,todo,len-todo); + } else + user += track.write(lin,0,len); + + seq = gseq; + + if (user >= luser + 8000 && Call.call_state == Call.UA_STATE_INCALL) { + if (luser == -8000 || am.getMode() != speakermode) { + saveVolume(); + setMode(speakermode); + restoreVolume(); + } + luser = user; + } + lserver = server; + } + } + track.stop(); + saveVolume(); + setStreamVolume(AudioManager.STREAM_MUSIC,oldvol,0); + restoreSettings(); + setStreamVolume(AudioManager.STREAM_MUSIC,oldvol,0); + tg.stopTone(); + tg = new ToneGenerator(AudioManager.STREAM_RING,ToneGenerator.MAX_VOLUME/4*3); + tg.startTone(ToneGenerator.TONE_PROP_PROMPT); + try { + sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + tg.stopTone(); + + rtp_socket.close(); + rtp_socket = null; + + if (DEBUG) + println("rtp receiver terminated"); + } + + /** Debug output */ + private static void println(String str) { + System.out.println("RtpStreamReceiver: " + str); + } + + public static int byte2int(byte b) { // return (b>=0)? b : -((b^0xFF)+1); + // return (b>=0)? b : b+0x100; + return (b + 0x100) % 0x100; + } + + public static int byte2int(byte b1, byte b2) { + return (((b1 + 0x100) % 0x100) << 8) + (b2 + 0x100) % 0x100; + } +} diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/media/RtpStreamSender.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/RtpStreamSender.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2005 Luca Veltri - University of Parma - Italy + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.media; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.util.Random; + + +import org.sipdroid.pjlib.Codec; + +import org.sipdroid.media.RtpStreamReceiver; +import org.sipdroid.net.RtpPacket; +import org.sipdroid.net.RtpSocket; +import org.sipdroid.net.SipdroidSocket; + +import com.beem.project.beem.ui.Call; +import com.beem.project.beem.utils.BeemConnectivity; + +import android.content.Context; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.MediaRecorder; +import android.preference.PreferenceManager; +import android.provider.Settings; +import android.telephony.TelephonyManager; + +/** + * RtpStreamSender is a generic stream sender. It takes an InputStream and sends + * it through RTP. + */ +public class RtpStreamSender extends Thread { + /** Whether working in debug mode. */ + public static boolean DEBUG = true; + + /** The RtpSocket */ + RtpSocket rtp_socket = null; + + /** Payload type */ + int p_type; + + /** Number of frame per second */ + long frame_rate; + + /** Number of bytes per frame */ + int frame_size; + + /** + * Whether it works synchronously with a local clock, or it it acts as slave + * of the InputStream + */ + boolean do_sync = true; + + /** + * Synchronization correction value, in milliseconds. It accellarates the + * sending rate respect to the nominal value, in order to compensate program + * latencies. + */ + int sync_adj = 0; + + /** Whether it is running */ + boolean running = false; + boolean muted = false; + + /** + * Constructs a RtpStreamSender. + * + * @param input_stream + * the stream to be sent + * @param do_sync + * whether time synchronization must be performed by the + * RtpStreamSender, or it is performed by the InputStream (e.g. + * the system audio input) + * @param payload_type + * the payload type + * @param frame_rate + * the frame rate, i.e. the number of frames that should be sent + * per second; it is used to calculate the nominal packet time + * and,in case of do_sync==true, the next departure time + * @param frame_size + * the size of the payload + * @param src_socket + * the socket used to send the RTP packet + * @param dest_addr + * the destination address + * @param dest_port + * the destination port + */ + public RtpStreamSender(boolean do_sync, + int payload_type, long frame_rate, int frame_size, + SipdroidSocket src_socket, String dest_addr, int dest_port) { + init(do_sync, payload_type, frame_rate, frame_size, + src_socket, dest_addr, dest_port); + } + + /** Inits the RtpStreamSender */ + private void init(boolean do_sync, + int payload_type, long frame_rate, int frame_size, + SipdroidSocket src_socket, String dest_addr, + int dest_port) { + this.p_type = payload_type; + this.frame_rate = frame_rate; + this.frame_size = frame_size; + this.do_sync = do_sync; + try { + rtp_socket = new RtpSocket(src_socket, InetAddress + .getByName(dest_addr), dest_port); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** Sets the synchronization adjustment time (in milliseconds). */ + public void setSyncAdj(int millisecs) { + sync_adj = millisecs; + } + + /** Whether is running */ + public boolean isRunning() { + return running; + } + + public boolean mute() { + return muted = !muted; + } + + public static int delay = 0; + + /** Stops running */ + public void halt() { + running = false; + } + + Random random; + double smin = 200,s; + int nearend; + + void calc(short[] lin,int off,int len) { + int i,j; + double sm = 30000,r; + + for (i = 0; i < len; i += 5) { + j = lin[i+off]; + s = 0.03*Math.abs(j) + 0.97*s; + if (s < sm) sm = s; + if (s > smin) nearend = 3000/5; + else if (nearend > 0) nearend--; + } + for (i = 0; i < len; i++) { + j = lin[i+off]; + if (j > 6550) + lin[i+off] = 6550*5; + else if (j < -6550) + lin[i+off] = -6550*5; + else + lin[i+off] = (short)(j*5); + } + r = (double)len/100000; + smin = sm*r + smin*(1-r); + } + + void calc1(short[] lin,int off,int len) { + int i,j; + + for (i = 0; i < len; i++) { + j = lin[i+off]; + lin[i+off] = (short)(j>>1); + } + } + + void calc5(short[] lin,int off,int len) { + int i,j; + + for (i = 0; i < len; i++) { + j = lin[i+off]; + if (j > 16350) + lin[i+off] = 16350<<1; + else if (j < -16350) + lin[i+off] = -16350<<1; + else + lin[i+off] = (short)(j<<1); + } + } + + void calc10(short[] lin,int off,int len) { + int i,j; + + for (i = 0; i < len; i++) { + j = lin[i+off]; + if (j > 8150) + lin[i+off] = 8150<<2; + else if (j < -8150) + lin[i+off] = -8150<<2; + else + lin[i+off] = (short)(j<<2); + } + } + + void noise(short[] lin,int off,int len,double power) { + int i,r = (int)(power*2); + short ran; + + if (r == 0) r = 1; + for (i = 0; i < len; i += 4) { + ran = (short)(random.nextInt(r*2)-r); + lin[i+off] = ran; + lin[i+off+1] = ran; + lin[i+off+2] = ran; + lin[i+off+3] = ran; + } + } + public static float getMicGain() { + if (Call.headset > 0) + return Float.valueOf(PreferenceManager.getDefaultSharedPreferences(Call.mContext).getString("hmicgain", "1.0")); + return Float.valueOf(PreferenceManager.getDefaultSharedPreferences(Call.mContext).getString("micgain", "0.25")); + } + + /** Runs it in a new Thread. */ + public void run() { + if (rtp_socket == null) + return; + byte[] buffer = new byte[frame_size + 12]; + RtpPacket rtp_packet = new RtpPacket(buffer, 0); + rtp_packet.setPayloadType(p_type); + int seqn = 0; + long time = 0; + double p = 0; + TelephonyManager tm = (TelephonyManager) Call.mContext.getSystemService(Context.TELEPHONY_SERVICE); + boolean improve = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("improve",false); + boolean useGSM = !PreferenceManager.getDefaultSharedPreferences(Call.mContext).getString("compression","edge").equals("never"); + int micgain = (int)(getMicGain()*10); + running = true; + + if (DEBUG) + println("Reading blocks of " + buffer.length + " bytes"); + + android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); + AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, + AudioRecord.getMinBufferSize(8000, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioFormat.ENCODING_PCM_16BIT)*3/2); + short[] lin = new short[frame_size*11]; + int num,ring = 0; + random = new Random(); + InputStream alerting = null; + try { + alerting = Call.mContext.getAssets().open("alerting"); + } catch (IOException e2) { + e2.printStackTrace(); + } + switch (p_type) { + case 3: + Codec.init(); + break; + case 0: + case 8: + G711.init(); + break; + } + record.startRecording(); + while (running) { + if (muted || Call.call_state == Call.UA_STATE_HOLD) { + record.stop(); + while (running && (muted || Call.call_state == Call.UA_STATE_HOLD)) { + try { + sleep(1000); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + record.startRecording(); + } + num = record.read(lin,(ring+delay)%(frame_size*11),frame_size); + + if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL) { + calc(lin,(ring+delay)%(frame_size*11),num); + if (RtpStreamReceiver.nearend != 0) + noise(lin,(ring+delay)%(frame_size*11),num,p); + else if (RtpStreamReceiver.nearend == 0) + p = 0.9*p + 0.1*s; + } else switch (micgain) { + case 1: + calc1(lin,(ring+delay)%(frame_size*11),num); + break; + case 5: + calc5(lin,(ring+delay)%(frame_size*11),num); + break; + case 10: + calc10(lin,(ring+delay)%(frame_size*11),num); + break; + } + if (Call.call_state != Call.UA_STATE_INCALL && alerting != null) { + try { + if (alerting.available() < num) + alerting.reset(); + alerting.read(buffer,12,num); + } catch (IOException e) { + e.printStackTrace(); + } + switch (p_type) {// have to add ulaw case? + case 3: + G711.alaw2linear(buffer, lin, num); + num = Codec.encode(lin, 0, buffer, num); + break; + case 0: + G711.alaw2linear(buffer, lin, num); + G711.linear2ulaw(lin, 0, buffer, num); + break; + } + } else { + switch (p_type) { + case 3: + num = Codec.encode(lin, ring%(frame_size*11), buffer, num); + break; + case 0: + G711.linear2ulaw(lin, ring%(frame_size*11), buffer, num); + break; + case 8: + G711.linear2alaw(lin, ring%(frame_size*11), buffer, num); + break; + } + } + ring += frame_size; + rtp_packet.setSequenceNumber(seqn++); + rtp_packet.setTimestamp(time); + rtp_packet.setPayloadLength(num); + try { + rtp_socket.send(rtp_packet); + } catch (IOException e) { + e.printStackTrace(); + } + time += frame_size; + /*if (useGSM && p_type == 8 && !BeemConnectivity.isWifi(Call.mContext) && tm.getNetworkType() == TelephonyManager.NETWORK_TYPE_EDGE) { + rtp_packet.setPayloadType(p_type = 3); + if (frame_size == 1024) { + frame_size = 960; + ring = 0; + } + }*/ + } + record.stop(); + + rtp_socket.close(); + rtp_socket = null; + + if (DEBUG) + println("rtp sender terminated"); + } + + /** Debug output */ + private static void println(String str) { + System.out.println("RtpStreamSender: " + str); + } + +} \ No newline at end of file diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/net/RtpPacket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/RtpPacket.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2005 Luca Veltri - University of Parma - Italy + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.net; + +import com.beem.project.beem.utils.Random; + +/** + * RtpPacket implements a RTP packet. + */ +public class RtpPacket { + /* RTP packet buffer containing both the RTP header and payload */ + byte[] packet; + + /* RTP packet length */ + int packet_len; + + /* RTP header length */ + // int header_len; + /** Gets the RTP packet */ + public byte[] getPacket() { + return packet; + } + + /** Gets the RTP packet length */ + public int getLength() { + return packet_len; + } + + /** Gets the RTP header length */ + public int getHeaderLength() { + if (packet_len >= 12) + return 12 + 4 * getCscrCount(); + else + return packet_len; // broken packet + } + + /** Gets the RTP header length */ + public int getPayloadLength() { + if (packet_len >= 12) + return packet_len - getHeaderLength(); + else + return 0; // broken packet + } + + /** Sets the RTP payload length */ + public void setPayloadLength(int len) { + packet_len = getHeaderLength() + len; + } + + // version (V): 2 bits + // padding (P): 1 bit + // extension (X): 1 bit + // CSRC count (CC): 4 bits + // marker (M): 1 bit + // payload type (PT): 7 bits + // sequence number: 16 bits + // timestamp: 32 bits + // SSRC: 32 bits + // CSRC list: 0 to 15 items, 32 bits each + + /** Gets the version (V) */ + public int getVersion() { + if (packet_len >= 12) + return (packet[0] >> 6 & 0x03); + else + return 0; // broken packet + } + + /** Sets the version (V) */ + public void setVersion(int v) { + if (packet_len >= 12) + packet[0] = (byte) ((packet[0] & 0x3F) | ((v & 0x03) << 6)); + } + + /** Whether has padding (P) */ + public boolean hasPadding() { + if (packet_len >= 12) + return getBit(packet[0], 5); + else + return false; // broken packet + } + + /** Set padding (P) */ + public void setPadding(boolean p) { + if (packet_len >= 12) + packet[0] = setBit(p, packet[0], 5); + } + + /** Whether has extension (X) */ + public boolean hasExtension() { + if (packet_len >= 12) + return getBit(packet[0], 4); + else + return false; // broken packet + } + + /** Set extension (X) */ + public void setExtension(boolean x) { + if (packet_len >= 12) + packet[0] = setBit(x, packet[0], 4); + } + + /** Gets the CSCR count (CC) */ + public int getCscrCount() { + if (packet_len >= 12) + return (packet[0] & 0x0F); + else + return 0; // broken packet + } + + /** Whether has marker (M) */ + public boolean hasMarker() { + if (packet_len >= 12) + return getBit(packet[1], 7); + else + return false; // broken packet + } + + /** Set marker (M) */ + public void setMarker(boolean m) { + if (packet_len >= 12) + packet[1] = setBit(m, packet[1], 7); + } + + /** Gets the payload type (PT) */ + public int getPayloadType() { + if (packet_len >= 12) + return (packet[1] & 0x7F); + else + return -1; // broken packet + } + + /** Sets the payload type (PT) */ + public void setPayloadType(int pt) { + if (packet_len >= 12) + packet[1] = (byte) ((packet[1] & 0x80) | (pt & 0x7F)); + } + + /** Gets the sequence number */ + public int getSequenceNumber() { + if (packet_len >= 12) + return getInt(packet, 2, 4); + else + return 0; // broken packet + } + + /** Sets the sequence number */ + public void setSequenceNumber(int sn) { + if (packet_len >= 12) + setInt(sn, packet, 2, 4); + } + + /** Gets the timestamp */ + public long getTimestamp() { + if (packet_len >= 12) + return getLong(packet, 4, 8); + else + return 0; // broken packet + } + + /** Sets the timestamp */ + public void setTimestamp(long timestamp) { + if (packet_len >= 12) + setLong(timestamp, packet, 4, 8); + } + + /** Gets the SSCR */ + public long getSscr() { + if (packet_len >= 12) + return getLong(packet, 8, 12); + else + return 0; // broken packet + } + + /** Sets the SSCR */ + public void setSscr(long ssrc) { + if (packet_len >= 12) + setLong(ssrc, packet, 8, 12); + } + + /** Gets the CSCR list */ + public long[] getCscrList() { + int cc = getCscrCount(); + long[] cscr = new long[cc]; + for (int i = 0; i < cc; i++) + cscr[i] = getLong(packet, 12 + 4 * i, 16 + 4 * i); + return cscr; + } + + /** Sets the CSCR list */ + public void setCscrList(long[] cscr) { + if (packet_len >= 12) { + int cc = cscr.length; + if (cc > 15) + cc = 15; + packet[0] = (byte) (((packet[0] >> 4) << 4) + cc); + cscr = new long[cc]; + for (int i = 0; i < cc; i++) + setLong(cscr[i], packet, 12 + 4 * i, 16 + 4 * i); + // header_len=12+4*cc; + } + } + + /** Sets the payload */ + public void setPayload(byte[] payload, int len) { + if (packet_len >= 12) { + int header_len = getHeaderLength(); + for (int i = 0; i < len; i++) + packet[header_len + i] = payload[i]; + packet_len = header_len + len; + } + } + + /** Gets the payload */ + public byte[] getPayload() { + int header_len = getHeaderLength(); + int len = packet_len - header_len; + byte[] payload = new byte[len]; + for (int i = 0; i < len; i++) + payload[i] = packet[header_len + i]; + return payload; + } + + /** Creates a new RTP packet */ + public RtpPacket(byte[] buffer, int packet_length) { + packet = buffer; + packet_len = packet_length; + if (packet_len < 12) + packet_len = 12; + init(0x0F); + } + + /** init the RTP packet header (only PT) */ + public void init(int ptype) { + init(ptype, Random.nextLong()); + } + + /** init the RTP packet header (PT and SSCR) */ + public void init(int ptype, long sscr) { + init(ptype, Random.nextInt(), Random.nextLong(), sscr); + } + + /** init the RTP packet header (PT, SQN, TimeStamp, SSCR) */ + public void init(int ptype, int seqn, long timestamp, long sscr) { + setVersion(2); + setPayloadType(ptype); + setSequenceNumber(seqn); + setTimestamp(timestamp); + setSscr(sscr); + } + + // *********************** Private and Static *********************** + + /** Gets int value */ + private static int getInt(byte b) { + return ((int) b + 256) % 256; + } + + /** Gets long value */ + private static long getLong(byte[] data, int begin, int end) { + long n = 0; + for (; begin < end; begin++) { + n <<= 8; + n += data[begin]; + } + return n; + } + + /** Sets long value */ + private static void setLong(long n, byte[] data, int begin, int end) { + for (end--; end >= begin; end--) { + data[end] = (byte) (n % 256); + n >>= 8; + } + } + + /** Gets Int value */ + private static int getInt(byte[] data, int begin, int end) { + return (int) getLong(data, begin, end); + } + + /** Sets Int value */ + private static void setInt(int n, byte[] data, int begin, int end) { + setLong(n, data, begin, end); + } + + /** Gets bit value */ + private static boolean getBit(byte b, int bit) { + return (b >> bit) == 1; + } + + /** Sets bit value */ + private static byte setBit(boolean value, byte b, int bit) { + if (value) + return (byte) (b | (1 << bit)); + else + return (byte) ((b | (1 << bit)) ^ (1 << bit)); + } +} diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/net/RtpSocket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/RtpSocket.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2005 Luca Veltri - University of Parma - Italy + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.net; + +import java.net.InetAddress; +import java.net.DatagramPacket; +import java.io.IOException; + + + +/** + * RtpSocket implements a RTP socket for receiving and sending RTP packets. + *

+ * RtpSocket is associated to a DatagramSocket that is used to send and/or + * receive RtpPackets. + */ +public class RtpSocket { + /** UDP socket */ + SipdroidSocket socket; + DatagramPacket datagram; + + /** Remote address */ + InetAddress r_addr; + + /** Remote port */ + int r_port; + + /** Creates a new RTP socket (only receiver) */ + public RtpSocket(SipdroidSocket datagram_socket) { + socket = datagram_socket; + r_addr = null; + r_port = 0; + datagram = new DatagramPacket(new byte[1],1); + } + + /** Creates a new RTP socket (sender and receiver) */ + public RtpSocket(SipdroidSocket datagram_socket, + InetAddress remote_address, int remote_port) { + socket = datagram_socket; + r_addr = remote_address; + r_port = remote_port; + datagram = new DatagramPacket(new byte[1],1); + } + + /** Returns the RTP SipdroidSocket */ + public SipdroidSocket getDatagramSocket() { + return socket; + } + + /** Receives a RTP packet from this socket */ + public void receive(RtpPacket rtpp) throws IOException { + datagram.setData(rtpp.packet); + datagram.setLength(rtpp.packet.length); + socket.receive(datagram); + if (!socket.isConnected()) + socket.connect(datagram.getAddress(),datagram.getPort()); + rtpp.packet_len = datagram.getLength(); + } + + /** Sends a RTP packet from this socket */ + public void send(RtpPacket rtpp) throws IOException { + datagram.setData(rtpp.packet); + datagram.setLength(rtpp.packet_len); + datagram.setAddress(r_addr); + datagram.setPort(r_port); + socket.send(datagram); + } + + /** Closes this socket */ + public void close() { // socket.close(); + } + +} diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/net/SipdroidSocket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/SipdroidSocket.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.net; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.SocketOptions; +import java.net.UnknownHostException; + +import org.sipdroid.net.impl.OSNetworkSystem; +import org.sipdroid.net.impl.PlainDatagramSocketImpl; + +public class SipdroidSocket extends DatagramSocket { + + PlainDatagramSocketImpl impl; + public static boolean loaded = false; + + public SipdroidSocket(int port) throws SocketException, UnknownHostException { + super(!loaded?port:0); + if (loaded) { + impl = new PlainDatagramSocketImpl(); + impl.create(); + impl.bind(port,InetAddress.getByName("0")); + } + } + + public void close() { + super.close(); + if (loaded) impl.close(); + } + + public void setSoTimeout(int val) throws SocketException { + if (loaded) impl.setOption(SocketOptions.SO_TIMEOUT, val); + else super.setSoTimeout(val); + } + + public void receive(DatagramPacket pack) throws IOException { + if (loaded) impl.receive(pack); + else super.receive(pack); + } + + public void send(DatagramPacket pack) throws IOException { + if (loaded) impl.send(pack); + else super.send(pack); + } + + public boolean isConnected() { + if (loaded) return true; + else return super.isConnected(); + } + + public void disconnect() { + if (!loaded) super.disconnect(); + } + + public void connect(InetAddress addr,int port) { + if (!loaded) super.connect(addr,port); + } + + static { + try { + System.loadLibrary("OSNetworkSystem"); + OSNetworkSystem.getOSNetworkSystem().oneTimeInitialization(true); + SipdroidSocket.loaded = true; + } catch (Throwable e) { + e.printStackTrace(); + } + } +} diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/net/impl/OSNetworkSystem.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/impl/OSNetworkSystem.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,748 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// BEGIN android-note +// address length was changed from long to int for performance reasons. +// END android-note + +package org.sipdroid.net.impl; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.SocketImpl; +import java.net.UnknownHostException; +import java.nio.channels.Channel; +// BEGIN android-removed +// import java.nio.channels.SelectableChannel; +// END android-removed +/* + * + * This Class is used for native code wrap, the implement class of + * INetworkSystem. + * + */ +public final class OSNetworkSystem { + + // ---------------------------------------------------- + // Class Variables + // ---------------------------------------------------- + + private static final int ERRORCODE_SOCKET_TIMEOUT = -209; + + private static OSNetworkSystem ref = new OSNetworkSystem(); + + private static final int INETADDR_REACHABLE = 0; + + private static boolean isNetworkInited = false; + + // ---------------------------------------------------- + // Class Constructor + // ---------------------------------------------------- + + // can not be instantiated. + private OSNetworkSystem() { + super(); + } + + /* + * @return a static ref of this class + */ + public static OSNetworkSystem getOSNetworkSystem() { + return ref; + } + + // Useing when cache set/get is OK + // public static native void oneTimeInitializationDatagram( + // boolean jcl_IPv6_support); + // + // public static native void oneTimeInitializationSocket( + // boolean jcl_IPv6_support); + + // -------------------------------------------------- + // java codes that wrap native codes + // -------------------------------------------------- + + public void createSocket(FileDescriptor fd, boolean preferIPv4Stack) + throws IOException { + createSocketImpl(fd, preferIPv4Stack); + } + + public void createDatagramSocket(FileDescriptor fd, boolean preferIPv4Stack) + throws SocketException { + createDatagramSocketImpl(fd, preferIPv4Stack); + } + + public int read(FileDescriptor aFD, byte[] data, int offset, int count, + int timeout) throws IOException { + return readSocketImpl(aFD, data, offset, count, timeout); + } + + public int readDirect(FileDescriptor aFD, int address, int offset, int count, + int timeout) throws IOException { + return readSocketDirectImpl(aFD, address, offset, count, timeout); + } + + public int write(FileDescriptor aFD, byte[] data, int offset, int count) + throws IOException { + return writeSocketImpl(aFD, data, offset, count); + } + + public int writeDirect(FileDescriptor aFD, int address, int offset, + int count) throws IOException { + return writeSocketDirectImpl(aFD, address, offset, count); + } + + public void setNonBlocking(FileDescriptor aFD, boolean block) + throws IOException { + setNonBlockingImpl(aFD, block); + } + + public void connectDatagram(FileDescriptor aFD, int port, int trafficClass, + InetAddress inetAddress) throws SocketException { + connectDatagramImpl2(aFD, port, trafficClass, inetAddress); + } + + public int connect(FileDescriptor aFD, int trafficClass, + InetAddress inetAddress, int port) throws IOException{ + return connectSocketImpl(aFD, trafficClass, inetAddress, port); + } + + // BEGIN android-changed + public int connectWithTimeout(FileDescriptor aFD, int timeout, + int trafficClass, InetAddress inetAddress, int port, int step, + byte[] context) throws IOException{ + return connectWithTimeoutSocketImpl(aFD, timeout, trafficClass, + inetAddress, port, step, context); + } + // END android-changed + + public void connectStreamWithTimeoutSocket(FileDescriptor aFD, int aport, + int timeout, int trafficClass, InetAddress inetAddress) + throws IOException { + connectStreamWithTimeoutSocketImpl(aFD, aport, timeout, trafficClass, + inetAddress); + } + + public void bind(FileDescriptor aFD, int port, InetAddress inetAddress) + throws SocketException { + socketBindImpl(aFD, port, inetAddress); + } + + public boolean bind2(FileDescriptor aFD, int port, boolean bindToDevice, + InetAddress inetAddress) throws SocketException { + return socketBindImpl2(aFD, port, bindToDevice, inetAddress); + } + + public void accept(FileDescriptor fdServer, SocketImpl newSocket, + FileDescriptor fdnewSocket, int timeout) throws IOException { + acceptSocketImpl(fdServer, newSocket, fdnewSocket, timeout); + } + + public int sendDatagram(FileDescriptor fd, byte[] data, int offset, + int length, int port, boolean bindToDevice, int trafficClass, + InetAddress inetAddress) throws IOException { + return sendDatagramImpl(fd, data, offset, length, port, bindToDevice, + trafficClass, inetAddress); + } + + public int sendDatagramDirect(FileDescriptor fd, int address, int offset, + int length, int port, boolean bindToDevice, int trafficClass, + InetAddress inetAddress) throws IOException { + return sendDatagramDirectImpl(fd, address, offset, length, port, bindToDevice, + trafficClass, inetAddress); + } + + public int sendDatagram2(FileDescriptor fd, byte[] data, int offset, + int length, int port, InetAddress inetAddress) throws IOException { + return sendDatagramImpl2(fd, data, offset, length, port, inetAddress); + } + + public int receiveDatagram(FileDescriptor aFD, DatagramPacket packet, + byte[] data, int offset, int length, int receiveTimeout, + boolean peek) throws IOException { + return receiveDatagramImpl(aFD, packet, data, offset, length, + receiveTimeout, peek); + } + + public int receiveDatagramDirect(FileDescriptor aFD, DatagramPacket packet, + int address, int offset, int length, int receiveTimeout, + boolean peek) throws IOException { + return receiveDatagramDirectImpl(aFD, packet, address, offset, length, + receiveTimeout, peek); + } + + public int recvConnectedDatagram(FileDescriptor aFD, DatagramPacket packet, + byte[] data, int offset, int length, int receiveTimeout, + boolean peek) throws IOException { + return recvConnectedDatagramImpl(aFD, packet, data, offset, length, + receiveTimeout, peek); + } + + public int recvConnectedDatagramDirect(FileDescriptor aFD, DatagramPacket packet, int address, + int offset, int length, int receiveTimeout, boolean peek) + throws IOException { + return recvConnectedDatagramDirectImpl(aFD, packet, address, offset, length, receiveTimeout, peek); + } + + public int peekDatagram(FileDescriptor aFD, InetAddress sender, + int receiveTimeout) throws IOException { + return peekDatagramImpl(aFD, sender, receiveTimeout); + } + + public int sendConnectedDatagram(FileDescriptor fd, byte[] data, + int offset, int length, boolean bindToDevice) throws IOException { + return sendConnectedDatagramImpl(fd, data, offset, length, bindToDevice); + } + + public int sendConnectedDatagramDirect(FileDescriptor fd, int address, + int offset, int length, boolean bindToDevice) throws IOException { + return sendConnectedDatagramDirectImpl(fd, address, offset, length, bindToDevice); + } + + public void disconnectDatagram(FileDescriptor aFD) throws SocketException { + disconnectDatagramImpl(aFD); + } + + public void createMulticastSocket(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException { + createMulticastSocketImpl(aFD, preferIPv4Stack); + } + + public void createServerStreamSocket(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException { + createServerStreamSocketImpl(aFD, preferIPv4Stack); + } + + public int receiveStream(FileDescriptor aFD, byte[] data, int offset, + int count, int timeout) throws IOException { + return receiveStreamImpl(aFD, data, offset, count, timeout); + } + + public int sendStream(FileDescriptor fd, byte[] data, int offset, int count) + throws IOException { + return sendStreamImpl(fd, data, offset, count); + } + + public void shutdownInput(FileDescriptor descriptor) throws IOException { + shutdownInputImpl(descriptor); + } + + public void shutdownOutput(FileDescriptor descriptor) throws IOException { + shutdownOutputImpl(descriptor); + } + + public boolean supportsUrgentData(FileDescriptor fd) { + return supportsUrgentDataImpl(fd); + } + + public void sendUrgentData(FileDescriptor fd, byte value) { + sendUrgentDataImpl(fd, value); + } + + public int availableStream(FileDescriptor aFD) throws SocketException { + return availableStreamImpl(aFD); + } + + // BEGIN android-removed + // public void acceptStreamSocket(FileDescriptor fdServer, + // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + // throws IOException { + // acceptStreamSocketImpl(fdServer, newSocket, fdnewSocket, timeout); + // } + // + // public void createStreamSocket(FileDescriptor aFD, boolean preferIPv4Stack) + // throws SocketException { + // createStreamSocketImpl(aFD, preferIPv4Stack); + // } + // END android-removed + + public void listenStreamSocket(FileDescriptor aFD, int backlog) + throws SocketException { + listenStreamSocketImpl(aFD, backlog); + } + + // BEGIN android-removed + // public boolean isReachableByICMP(final InetAddress dest, + // InetAddress source, final int ttl, final int timeout) { + // return INETADDR_REACHABLE == isReachableByICMPImpl(dest, source, ttl, + // timeout); + // } + // END android-removed + + /* + * + * @param + * readChannels all channels interested in read and accept + * @param + * writeChannels all channels interested in write and connect + * @param timeout + * timeout in millis @return a set of channels that are ready for operation + * @throws + * SocketException @return int array, each int approve one of the * channel if OK + */ + + public int[] select(FileDescriptor[] readFDs, + FileDescriptor[] writeFDs, long timeout) + throws SocketException { + int countRead = readFDs.length; + int countWrite = writeFDs.length; + int result = 0; + if (0 == countRead + countWrite) { + return (new int[0]); + } + int[] flags = new int[countRead + countWrite]; + + // handle timeout in native + result = selectImpl(readFDs, writeFDs, countRead, countWrite, flags, + timeout); + + if (0 <= result) { + return flags; + } + if (ERRORCODE_SOCKET_TIMEOUT == result) { + return new int[0]; + } + throw new SocketException(); + + } + + public InetAddress getSocketLocalAddress(FileDescriptor aFD, + boolean preferIPv6Addresses) { + return getSocketLocalAddressImpl(aFD, preferIPv6Addresses); + } + + /* + * Query the IP stack for the local port to which this socket is bound. + * + * @param aFD the socket descriptor @param preferIPv6Addresses address + * preference for nodes that support both IPv4 and IPv6 @return int the + * local port to which the socket is bound + */ + public int getSocketLocalPort(FileDescriptor aFD, + boolean preferIPv6Addresses) { + return getSocketLocalPortImpl(aFD, preferIPv6Addresses); + } + + /* + * Query the IP stack for the nominated socket option. + * + * @param aFD the socket descriptor @param opt the socket option type + * @return the nominated socket option value + * + * @throws SocketException if the option is invalid + */ + public Object getSocketOption(FileDescriptor aFD, int opt) + throws SocketException { + return getSocketOptionImpl(aFD, opt); + } + + /* + * Set the nominated socket option in the IP stack. + * + * @param aFD the socket descriptor @param opt the option selector @param + * optVal the nominated option value + * + * @throws SocketException if the option is invalid or cannot be set + */ + public void setSocketOption(FileDescriptor aFD, int opt, Object optVal) + throws SocketException { + setSocketOptionImpl(aFD, opt, optVal); + } + + public int getSocketFlags() { + return getSocketFlagsImpl(); + } + + /* + * Close the socket in the IP stack. + * + * @param aFD the socket descriptor + */ + public void socketClose(FileDescriptor aFD) throws IOException { + socketCloseImpl(aFD); + } + + public InetAddress getHostByAddr(byte[] addr) throws UnknownHostException { + return getHostByAddrImpl(addr); + } + + public InetAddress getHostByName(String addr, boolean preferIPv6Addresses) + throws UnknownHostException { + return getHostByNameImpl(addr, preferIPv6Addresses); + } + + public void setInetAddress(InetAddress sender, byte[] address) { + setInetAddressImpl(sender, address); + } + + // --------------------------------------------------- + // Native Codes + // --------------------------------------------------- + + static native void createSocketImpl(FileDescriptor fd, + boolean preferIPv4Stack); + + /* + * Allocate a datagram socket in the IP stack. The socket is associated with + * the aFD. + * + * @param aFD the FileDescriptor to associate with the socket @param + * preferIPv4Stack IP stack preference if underlying platform is V4/V6 + * @exception SocketException upon an allocation error + */ + static native void createDatagramSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; + + static native int readSocketImpl(FileDescriptor aFD, byte[] data, + int offset, int count, int timeout) throws IOException; + + static native int readSocketDirectImpl(FileDescriptor aFD, int address, + int offset, int count, int timeout) throws IOException; + + static native int writeSocketImpl(FileDescriptor fd, byte[] data, + int offset, int count) throws IOException; + + static native int writeSocketDirectImpl(FileDescriptor fd, int address, + int offset, int count) throws IOException; + + static native void setNonBlockingImpl(FileDescriptor aFD, + boolean block); + + static native int connectSocketImpl(FileDescriptor aFD, + int trafficClass, InetAddress inetAddress, int port); + + // BEGIN android-changed + static native int connectWithTimeoutSocketImpl( + FileDescriptor aFD, int timeout, int trafficClass, + InetAddress hostname, int port, int step, byte[] context); + // END android-changed + + static native void connectStreamWithTimeoutSocketImpl(FileDescriptor aFD, + int aport, int timeout, int trafficClass, InetAddress inetAddress) + throws IOException; + + static native void socketBindImpl(FileDescriptor aFD, int port, + InetAddress inetAddress) throws SocketException; + + static native void listenStreamSocketImpl(FileDescriptor aFD, int backlog) + throws SocketException; + + static native int availableStreamImpl(FileDescriptor aFD) + throws SocketException; + + static native void acceptSocketImpl(FileDescriptor fdServer, + SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + throws IOException; + + static native boolean supportsUrgentDataImpl(FileDescriptor fd); + + static native void sendUrgentDataImpl(FileDescriptor fd, byte value); + + /* + * Connect the socket to a port and address + * + * @param aFD the FileDescriptor to associate with the socket @param port + * the port to connect to @param trafficClass the traffic Class to be used + * then the connection is made @param inetAddress address to connect to. + * + * @exception SocketException if the connect fails + */ + static native void connectDatagramImpl2(FileDescriptor aFD, + int port, int trafficClass, InetAddress inetAddress) + throws SocketException; + + /* + * Disconnect the socket to a port and address + * + * @param aFD the FileDescriptor to associate with the socket + * + * @exception SocketException if the disconnect fails + */ + static native void disconnectDatagramImpl(FileDescriptor aFD) + throws SocketException; + + /* + * Allocate a datagram socket in the IP stack. The socket is associated with + * the aFD. + * + * @param aFD the FileDescriptor to associate with the socket @param + * preferIPv4Stack IP stack preference if underlying platform is V4/V6 + * @exception SocketException upon an allocation error + */ + + /* + * Bind the socket to the port/localhost in the IP stack. + * + * @param aFD the socket descriptor @param port the option selector @param + * bindToDevice bind the socket to the specified interface @param + * inetAddress address to connect to. @return if bind successful @exception + * SocketException thrown if bind operation fails + */ + static native boolean socketBindImpl2(FileDescriptor aFD, + int port, boolean bindToDevice, InetAddress inetAddress) + throws SocketException; + + /* + * Peek on the socket, update sender address and answer the + * sender port. + * + * @param aFD the socket FileDescriptor @param sender an InetAddress, to be + * updated with the sender's address @param receiveTimeout the maximum + * length of time the socket should block, reading @return int the sender + * port + * + * @exception IOException upon an read error or timeout + */ + static native int peekDatagramImpl(FileDescriptor aFD, + InetAddress sender, int receiveTimeout) throws IOException; + + /* + * Recieve data on the socket into the specified buffer. The packet fields + * data & length are passed in addition to + * packet to eliminate the JNI field access calls. + * + * @param aFD the socket FileDescriptor @param packet the DatagramPacket to + * receive into @param data the data buffer of the packet @param offset the + * offset in the data buffer @param length the length of the data buffer in + * the packet @param receiveTimeout the maximum length of time the socket + * should block, reading @param peek indicates to peek at the data @return + * number of data received @exception IOException upon an read error or + * timeout + */ + static native int receiveDatagramImpl(FileDescriptor aFD, + DatagramPacket packet, byte[] data, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + static native int receiveDatagramDirectImpl(FileDescriptor aFD, + DatagramPacket packet, int address, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + /* + * Recieve data on the connected socket into the specified buffer. The + * packet fields data & length are passed in + * addition to packet to eliminate the JNI field access + * calls. + * + * @param aFD the socket FileDescriptor @param packet the DatagramPacket to + * receive into @param data the data buffer of the packet @param offset the + * offset in the data buffer @param length the length of the data buffer in + * the packet @param receiveTimeout the maximum length of time the socket + * should block, reading @param peek indicates to peek at the data @return + * number of data received @exception IOException upon an read error or + * timeout + */ + static native int recvConnectedDatagramImpl(FileDescriptor aFD, + DatagramPacket packet, byte[] data, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + static native int recvConnectedDatagramDirectImpl(FileDescriptor aFD, + DatagramPacket packet, int address, int offset, int length, + int receiveTimeout, boolean peek) throws IOException; + + /* + * Send the data to the nominated target address + * and port. These values are derived from the + * DatagramPacket to reduce the field calls within JNI. + * + * @param fd the socket FileDescriptor @param data the data buffer of the + * packet @param offset the offset in the data buffer @param length the + * length of the data buffer in the packet @param port the target host port + * @param bindToDevice if bind to device @param trafficClass the traffic + * class to be used when the datagram is sent @param inetAddress address to + * connect to. @return number of data send + * + * @exception IOException upon an read error or timeout + */ + static native int sendDatagramImpl(FileDescriptor fd, + byte[] data, int offset, int length, int port, + boolean bindToDevice, int trafficClass, InetAddress inetAddress) + throws IOException; + + static native int sendDatagramDirectImpl(FileDescriptor fd, + int address, int offset, int length, int port, + boolean bindToDevice, int trafficClass, InetAddress inetAddress) + throws IOException; + + /* + * Send the data to the address and port to which the was + * connnected and port. + * + * @param fd the socket FileDescriptor @param data the data buffer of the + * packet @param offset the offset in the data buffer @param length the + * length of the data buffer in the packet @param bindToDevice not used, + * current kept in case needed as was the case for sendDatagramImpl @return + * number of data send @exception IOException upon an read error or timeout + */ + static native int sendConnectedDatagramImpl(FileDescriptor fd, + byte[] data, int offset, int length, boolean bindToDevice) + throws IOException; + + static native int sendConnectedDatagramDirectImpl(FileDescriptor fd, + int address, int offset, int length, boolean bindToDevice) + throws IOException; + + /* + * Answer the result of attempting to create a server stream socket in the + * IP stack. Any special options required for server sockets will be set by + * this method. + * + * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4 + * @exception SocketException if an error occurs while creating the socket + */ + static native void createServerStreamSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; + + /* + * Answer the result of attempting to create a multicast socket in the IP + * stack. Any special options required for server sockets will be set by + * this method. + * + * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4 + * @exception SocketException if an error occurs while creating the socket + */ + static native void createMulticastSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; + + /* + * Recieve at most count bytes into the buffer data + * at the offset on the socket. + * + * @param aFD the socket FileDescriptor @param data the receive buffer + * @param offset the offset into the buffer @param count the max number of + * bytes to receive @param timeout the max time the read operation should + * block waiting for data @return int the actual number of bytes read + * @throws IOException @exception SocketException if an error occurs while + * reading + */ + static native int receiveStreamImpl(FileDescriptor aFD, byte[] data, + int offset, int count, int timeout) throws IOException; + + /* + * Send count bytes from the buffer data at + * the offset, on the socket. + * + * @param fd + * + * @param data the send buffer @param offset the offset into the buffer + * @param count the number of bytes to receive @return int the actual number + * of bytes sent @throws IOException @exception SocketException if an error + * occurs while writing + */ + static native int sendStreamImpl(FileDescriptor fd, byte[] data, + int offset, int count) throws IOException; + + private native void shutdownInputImpl(FileDescriptor descriptor) + throws IOException; + + private native void shutdownOutputImpl(FileDescriptor descriptor) + throws IOException; + + // BEGIN android-removed + // static native void acceptStreamSocketImpl(FileDescriptor fdServer, + // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + // throws IOException; + // + // static native void createStreamSocketImpl(FileDescriptor aFD, + // boolean preferIPv4Stack) throws SocketException; + // END android-removed + + static native int sendDatagramImpl2(FileDescriptor fd, byte[] data, + int offset, int length, int port, InetAddress inetAddress) + throws IOException; + + static native int selectImpl(FileDescriptor[] readfd, + FileDescriptor[] writefd, int cread, int cwirte, int[] flags, + long timeout); + + static native InetAddress getSocketLocalAddressImpl(FileDescriptor aFD, + boolean preferIPv6Addresses); + + /* + * Query the IP stack for the local port to which this socket is bound. + * + * @param aFD the socket descriptor @param preferIPv6Addresses address + * preference for nodes that support both IPv4 and IPv6 @return int the + * local port to which the socket is bound + */ + static native int getSocketLocalPortImpl(FileDescriptor aFD, + boolean preferIPv6Addresses); + + /* + * Query the IP stack for the nominated socket option. + * + * @param aFD the socket descriptor @param opt the socket option type + * @return the nominated socket option value + * + * @throws SocketException if the option is invalid + */ + static native Object getSocketOptionImpl(FileDescriptor aFD, int opt) + throws SocketException; + + /* + * Set the nominated socket option in the IP stack. + * + * @param aFD the socket descriptor @param opt the option selector @param + * optVal the nominated option value + * + * @throws SocketException if the option is invalid or cannot be set + */ + static native void setSocketOptionImpl(FileDescriptor aFD, int opt, + Object optVal) throws SocketException; + + static native int getSocketFlagsImpl(); + + /* + * Close the socket in the IP stack. + * + * @param aFD the socket descriptor + */ + static native void socketCloseImpl(FileDescriptor aFD); + + static native InetAddress getHostByAddrImpl(byte[] addr) + throws UnknownHostException; + + static native InetAddress getHostByNameImpl(String addr, + boolean preferIPv6Addresses) throws UnknownHostException; + + native void setInetAddressImpl(InetAddress sender, byte[] address); + + // BEGIN android-removed + // native int isReachableByICMPImpl(InetAddress addr, InetAddress local, + // int ttl, int timeout); + // END android-removed + + native Channel inheritedChannelImpl(); + + public Channel inheritedChannel() { + return inheritedChannelImpl(); + } + + public void oneTimeInitialization(boolean jcl_supports_ipv6){ + if (!isNetworkInited){ + oneTimeInitializationImpl(jcl_supports_ipv6); + isNetworkInited = true; + } + } + + native void oneTimeInitializationImpl (boolean jcl_supports_ipv6); +} \ No newline at end of file diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/net/impl/PlainDatagramSocketImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/net/impl/PlainDatagramSocketImpl.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.sipdroid.net.impl; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.DatagramPacket; +import java.net.DatagramSocketImpl; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketOptions; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.security.AccessController; + +/** + * The default, concrete instance of datagram sockets. This class does not + * support security checks. Alternative types of DatagramSocketImpl's may be + * used by setting the impl.prefix system property. + */ +public class PlainDatagramSocketImpl extends DatagramSocketImpl { + + static final int MULTICAST_IF = 1; + + static final int MULTICAST_TTL = 2; + + static final int TCP_NODELAY = 4; + + static final int FLAG_SHUTDOWN = 8; + + private final static int SO_BROADCAST = 32; + + final static int IP_MULTICAST_ADD = 19; + + final static int IP_MULTICAST_DROP = 20; + + final static int IP_MULTICAST_TTL = 17; + + /** + * for datagram and multicast sockets we have to set REUSEADDR and REUSEPORT + * when REUSEADDR is set for other types of sockets we need to just set + * REUSEADDR therefore we have this other option which sets both if + * supported by the platform. this cannot be in SOCKET_OPTIONS because since + * it is a public interface it ends up being public even if it is not + * declared public + */ + static final int REUSEADDR_AND_REUSEPORT = 10001; + + private boolean bindToDevice; + + private byte[] ipaddress = { 0, 0, 0, 0 }; + + private int ttl = 1; + + private OSNetworkSystem netImpl = OSNetworkSystem.getOSNetworkSystem(); + + private volatile boolean isNativeConnected; + + public int receiveTimeout; + + public boolean streaming = true; + + public boolean shutdownInput; + + /** + * used to keep address to which the socket was connected to at the native + * level + */ + private InetAddress connectedAddress; + + private int connectedPort = -1; + + /** + * used to store the trafficClass value which is simply returned as the + * value that was set. We also need it to pass it to methods that specify an + * address packets are going to be sent to + */ + private int trafficClass; + + public PlainDatagramSocketImpl(FileDescriptor fd, int localPort) { + super(); + this.fd = fd; + this.localPort = localPort; + } + + public PlainDatagramSocketImpl() { + super(); + fd = new FileDescriptor(); + } + + @Override + public void bind(int port, InetAddress addr) throws SocketException { + String prop = null; //AccessController.doPrivileged(new PriviAction("bindToDevice")); //$NON-NLS-1$ + boolean useBindToDevice = prop != null && prop.toLowerCase().equals("true"); //$NON-NLS-1$ + bindToDevice = netImpl.bind2(fd, port, useBindToDevice, addr); + if (0 != port) { + localPort = port; + } else { +// localPort = netImpl.getSocketLocalPort(fd, NetUtil.preferIPv6Addresses()); + } + + try { + // Ignore failures + setOption(SO_BROADCAST, Boolean.TRUE); + } catch (IOException e) { + } + } + + @Override + public void close() { + synchronized (fd) { + if (fd.valid()) { + try { + netImpl.socketClose(fd); + } catch (IOException e) { + } + fd = new FileDescriptor(); + } + } + } + + @Override + public void create() throws SocketException { + netImpl.createDatagramSocket(fd, false); //NetUtil.preferIPv4Stack()); + } + + @Override + protected void finalize() { + close(); + } + + @Override + public Object getOption(int optID) throws SocketException { + if (optID == SocketOptions.SO_TIMEOUT) { + return Integer.valueOf(receiveTimeout); + } else if (optID == SocketOptions.IP_TOS) { + return Integer.valueOf(trafficClass); + } else { + // Call the native first so there will be + // an exception if the socket if closed. + Object result = netImpl.getSocketOption(fd, optID); + if (optID == SocketOptions.IP_MULTICAST_IF + && (netImpl.getSocketFlags() & MULTICAST_IF) != 0) { + try { + return InetAddress.getByAddress(ipaddress); + } catch (UnknownHostException e) { + return null; + } + } + return result; + } + } + + @Override + public int getTimeToLive() throws IOException { + // Call the native first so there will be an exception if the socket if + // closed. + int result = (((Byte) getOption(IP_MULTICAST_TTL)).byteValue()) & 0xFF; + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + return ttl; + } + return result; + } + + @Override + public byte getTTL() throws IOException { + // Call the native first so there will be an exception if the socket if + // closed. + byte result = ((Byte) getOption(IP_MULTICAST_TTL)).byteValue(); + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + return (byte) ttl; + } + return result; + } + + @Override + public void join(InetAddress addr) throws IOException { +// setOption(IP_MULTICAST_ADD, new GenericIPMreq(addr)); + } + + @Override + public void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException { + if (addr instanceof InetSocketAddress) { + InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); +// setOption(IP_MULTICAST_ADD, new GenericIPMreq(groupAddr, netInterface)); + } + } + + @Override + public void leave(InetAddress addr) throws IOException { +// setOption(IP_MULTICAST_DROP, new GenericIPMreq(addr)); + } + + @Override + public void leaveGroup(SocketAddress addr, NetworkInterface netInterface) + throws IOException { + if (addr instanceof InetSocketAddress) { + InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); +// setOption(IP_MULTICAST_DROP, new GenericIPMreq(groupAddr, netInterface)); + } + } + + @Override + protected int peek(InetAddress sender) throws IOException { + if (isNativeConnected) { + /* + * in this case we know the port and address from which the data + * must have be been received as the socket is connected. However, + * we still need to do the receive in order to know that there was + * data received. We use a short buffer as we don't actually need + * the packet, only the knowledge that it is there + */ + byte[] storageArray = new byte[10]; + DatagramPacket pack = new DatagramPacket(storageArray, storageArray.length); + netImpl.recvConnectedDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, true); // peek + // to set the sender ,we now use a native function + // sender.ipaddress = connectedAddress.getAddress(); + netImpl.setInetAddress(sender, connectedAddress.getAddress()); + return connectedPort; + } + return netImpl.peekDatagram(fd, sender, receiveTimeout); + } + + @Override + public void receive(DatagramPacket pack) throws java.io.IOException { + try { + if (isNativeConnected) { + // do not peek + netImpl.recvConnectedDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, false); + updatePacketRecvAddress(pack); + } else { + // receiveDatagramImpl2 + netImpl.receiveDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, false); + } + } catch (InterruptedIOException e) { + throw new SocketTimeoutException(e.getMessage()); + } + } + + @Override + public void send(DatagramPacket packet) throws IOException { + + if (isNativeConnected) { + netImpl.sendConnectedDatagram(fd, packet.getData(), packet.getOffset(), packet + .getLength(), bindToDevice); + } else { + // sendDatagramImpl2 + netImpl.sendDatagram(fd, packet.getData(), packet.getOffset(), packet.getLength(), + packet.getPort(), bindToDevice, trafficClass, packet.getAddress()); + } + } + + /** + * Set the nominated socket option. As the timeouts are not set as options + * in the IP stack, the value is stored in an instance field. + * + * @throws SocketException thrown if the option value is unsupported or + * invalid + */ + @Override + public void setOption(int optID, Object val) throws SocketException { + /* + * for datagram sockets on some platforms we have to set both the + * REUSEADDR AND REUSEPORT so for REUSEADDR set this option option which + * tells the VM to set the two values as appropriate for the platform + */ + if (optID == SocketOptions.SO_REUSEADDR) { + optID = REUSEADDR_AND_REUSEPORT; + } + + if (optID == SocketOptions.SO_TIMEOUT) { + receiveTimeout = ((Integer) val).intValue(); + } else { + int flags = netImpl.getSocketFlags(); + try { + netImpl.setSocketOption(fd, optID | (flags << 16), val); + } catch (SocketException e) { + // we don't throw an exception for IP_TOS even if the platform + // won't let us set the requested value + if (optID != SocketOptions.IP_TOS) { + throw e; + } + } + if (optID == SocketOptions.IP_MULTICAST_IF && (flags & MULTICAST_IF) != 0) { + InetAddress inet = (InetAddress) val; +// if (NetUtil.bytesToInt(inet.getAddress(), 0) == 0 || inet.isLoopbackAddress()) { +// ipaddress = ((InetAddress) val).getAddress(); +// } else + { + InetAddress local = null; + try { + local = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + throw new SocketException("getLocalHost(): " + e.toString()); + } + if (inet.equals(local)) { + ipaddress = ((InetAddress) val).getAddress(); + } else { + throw new SocketException(val + " != getLocalHost(): " + local); + } + } + } + /* + * save this value as it is actually used differently for IPv4 and + * IPv6 so we cannot get the value using the getOption. The option + * is actually only set for IPv4 and a masked version of the value + * will be set as only a subset of the values are allowed on the + * socket. Therefore we need to retain it to return the value that + * was set. We also need the value to be passed into a number of + * natives so that it can be used properly with IPv6 + */ + if (optID == SocketOptions.IP_TOS) { + trafficClass = ((Integer) val).intValue(); + } + } + } + + @Override + public void setTimeToLive(int ttl) throws java.io.IOException { + setOption(IP_MULTICAST_TTL, Byte.valueOf((byte) (ttl & 0xFF))); + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + this.ttl = ttl; + } + } + + @Override + public void setTTL(byte ttl) throws java.io.IOException { + setOption(IP_MULTICAST_TTL, Byte.valueOf(ttl)); + if ((netImpl.getSocketFlags() & MULTICAST_TTL) != 0) { + this.ttl = ttl; + } + } + + @Override + public void connect(InetAddress inetAddr, int port) throws SocketException { + + // connectDatagram impl2 + netImpl.connectDatagram(fd, port, trafficClass, inetAddr); + + // if we get here then we are connected at the native level + try { + connectedAddress = InetAddress.getByAddress(inetAddr.getAddress()); + } catch (UnknownHostException e) { + // this is never expected to happen as we should not have gotten + // here if the address is not resolvable + throw new SocketException("K0317 "+inetAddr.getHostName()); //$NON-NLS-1$ + } + connectedPort = port; + isNativeConnected = true; + } + + @Override + public void disconnect() { + try { + netImpl.disconnectDatagram(fd); + } catch (Exception e) { + // there is currently no way to return an error so just eat any + // exception + } + connectedPort = -1; + connectedAddress = null; + isNativeConnected = false; + } + + @Override + public int peekData(DatagramPacket pack) throws IOException { + try { + if (isNativeConnected) { + netImpl.recvConnectedDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, true); // peek + updatePacketRecvAddress(pack); + } else { + // receiveDatagram 2 + netImpl.receiveDatagram(fd, pack, pack.getData(), pack.getOffset(), pack + .getLength(), receiveTimeout, true); // peek + } + } catch (InterruptedIOException e) { + throw new SocketTimeoutException(e.toString()); + } + return pack.getPort(); + } + + /** + * Set the received address and port in the packet. We do this when the + * Datagram socket is connected at the native level and the + * recvConnnectedDatagramImpl does not update the packet with address from + * which the packet was received + * + * @param packet + * the packet to be updated + */ + private void updatePacketRecvAddress(DatagramPacket packet) { + packet.setAddress(connectedAddress); + packet.setPort(connectedPort); + } + +} \ No newline at end of file diff -r 696b2880c994 -r 0e5b95573614 src/org/sipdroid/pjlib/Codec.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/pjlib/Codec.java Sat Dec 25 17:01:00 2010 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.sipdroid.pjlib; + +import java.lang.String; + +public class Codec { + public static native int open(String codec_id); + public static native int decode(byte alaw[], short lin[], int frames); + public static native int encode(short lin[], int offset, byte alaw[], int frames); + public static native int close(); + + public static void init() { + } + + public static boolean loaded; + + static { + try { + System.loadLibrary("pjlib_linker_jni"); + open("gsm"); + loaded = true; + } catch (Throwable e) { + e.printStackTrace(); + } + } +}