diff options
author | Andrew Comminos <andrewcomminos@gmail.com> | 2013-08-18 02:22:44 +0400 |
---|---|---|
committer | Andrew Comminos <andrewcomminos@gmail.com> | 2013-08-18 02:22:44 +0400 |
commit | 3f44098d66e3d117db6312f9e49ce6d43333f178 (patch) | |
tree | 9ffc0be00961015f25bc9941bd8de7c542077ccd /src/main/java | |
parent | f76b49c3e8072795e13f6bd4fb1634ab38514e10 (diff) |
More audio and Tor work, added libonionkit.
Diffstat (limited to 'src/main/java')
7 files changed, 128 insertions, 89 deletions
diff --git a/src/main/java/com/morlunk/jumble/Constants.java b/src/main/java/com/morlunk/jumble/Constants.java index eb9522b..83e6b54 100644 --- a/src/main/java/com/morlunk/jumble/Constants.java +++ b/src/main/java/com/morlunk/jumble/Constants.java @@ -31,9 +31,9 @@ public class Constants { static { // Load CELT bitstream versions from JNI. TODO clean me with renamed types for version - int error = 0; - com.morlunk.jumble.audio.celt11.SWIGTYPE_p_CELTMode celt11Mode = CELT11.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, new int[] { error }); - com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTMode celt7Mode = CELT7.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, new int[] { error }); + int[] error = new int[1]; + com.morlunk.jumble.audio.celt11.SWIGTYPE_p_CELTMode celt11Mode = CELT11.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, error); + com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTMode celt7Mode = CELT7.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, error); int[] celt11Version = new int[1]; int[] celt7Version = new int[1]; diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java index e458fd5..b68e5f7 100644 --- a/src/main/java/com/morlunk/jumble/JumbleService.java +++ b/src/main/java/com/morlunk/jumble/JumbleService.java @@ -181,6 +181,13 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } @Override + public void sendAccessTokens(List tokens) throws RemoteException { + Mumble.Authenticate.Builder ab = Mumble.Authenticate.newBuilder(); + ab.addAllTokens(tokens); + mConnection.sendTCPMessage(ab.build(), JumbleTCPMessageType.Authenticate); + } + + @Override public void requestBanList() throws RemoteException { } @@ -191,6 +198,20 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } @Override + public void requestComment(int session) throws RemoteException { + Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder(); + rbb.addSessionComment(session); + mConnection.sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob); + } + + @Override + public void requestChannelDescription(int channel) throws RemoteException { + Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder(); + rbb.addChannelDescription(channel); + mConnection.sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob); + } + + @Override public void registerUser(int session) throws RemoteException { Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); usb.setSession(session); @@ -347,6 +368,8 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon public void onConnectionEstablished() { Log.v(Constants.TAG, "Connected"); + mAudioOutput.startPlaying(); + notifyObservers(new ObserverRunnable() { @Override public void run(IJumbleObserver observer) throws RemoteException { diff --git a/src/main/java/com/morlunk/jumble/audio/AudioOutput.java b/src/main/java/com/morlunk/jumble/audio/AudioOutput.java index 740ddfb..830c91c 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioOutput.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioOutput.java @@ -19,6 +19,8 @@ package com.morlunk.jumble.audio; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; +import android.os.Handler; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -46,6 +48,7 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, private AudioTrack mAudioTrack; private Thread mThread; + private Object mInactiveLock = new Object(); // Lock that the audio thread waits on when there's no audio to play. Wake when we get a frame. private boolean mRunning = false; public AudioOutput(JumbleService service) { @@ -75,17 +78,32 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, @Override public void run() { - Log.v(Constants.TAG, "Started audio output"); + Log.v(Constants.TAG, "Started audio output thread."); android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); mRunning = true; + mAudioTrack.play(); final short[] mix = new short[Audio.FRAME_SIZE*12]; while(mRunning) { - mix(mix, mix.length); - mAudioTrack.write(mix, 0, mix.length); - mAudioTrack.play(); + boolean play = mix(mix, mix.length); + if(play) { + mAudioTrack.write(mix, 0, mix.length); + } else { + Log.v(Constants.TAG, "Pausing audio output thread."); + synchronized (mInactiveLock) { + try { + mInactiveLock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + Log.v(Constants.TAG, "Resuming audio output thread."); + } } + + mAudioTrack.flush(); + mAudioTrack.stop(); } private boolean mix(short[] outBuffer, int bufferSize) { @@ -95,7 +113,7 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, // TODO add priority speaker support for(AudioOutputSpeech speech : mAudioOutputs.values()) { - if(speech.needSamples(bufferSize)) + if(!speech.needSamples(bufferSize)) del.add(speech); else mix.add(speech); @@ -134,7 +152,7 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, if(user != null && !user.isLocalMuted()) { // TODO check for whispers here int seq = pds.next(); - ByteBuffer packet = ByteBuffer.allocate(pds.left() + 1); + ByteBuffer packet = ByteBuffer.allocate(pds.left() + 4); packet.putInt(msgFlags); packet.put(pds.dataBlock(pds.left())); @@ -149,6 +167,9 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, } aop.addFrameToBuffer(packet.array(), seq); + synchronized (mInactiveLock) { + mInactiveLock.notify(); + } } } @@ -156,11 +177,20 @@ public class AudioOutput extends JumbleMessageHandler.Stub implements Runnable, @Override public void onTalkStateUpdated(int session, User.TalkState state) { final User user = mService.getUserHandler().getUser(session); - mService.notifyObservers(new JumbleService.ObserverRunnable() { - @Override - public void run(IJumbleObserver observer) throws RemoteException { - observer.onUserTalkStateUpdated(user); - } - });; + if(user.getTalkState() != state) { + user.setTalkState(state); + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + mService.notifyObservers(new JumbleService.ObserverRunnable() { + @Override + public void run(IJumbleObserver observer) throws RemoteException { + observer.onUserTalkStateUpdated(user); + } + });; + } + }); + } } } diff --git a/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java b/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java index e56ff8b..3ce543b 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java @@ -16,6 +16,9 @@ package com.morlunk.jumble.audio; +import android.util.Log; + +import com.morlunk.jumble.Constants; import com.morlunk.jumble.audio.celt11.CELT11; import com.morlunk.jumble.audio.celt7.CELT7; import com.morlunk.jumble.audio.opus.Opus; @@ -93,6 +96,7 @@ public class AudioOutputSpeech { break; } + mBuffer = new float[mAudioBufferSize]; mJitterBuffer = Speex.jitter_buffer_init(Audio.FRAME_SIZE); Speex.jitter_buffer_ctl(mJitterBuffer, SpeexConstants.JITTER_BUFFER_SET_MARGIN, new int[] { 10 * Audio.FRAME_SIZE }); } @@ -154,7 +158,6 @@ public class AudioOutputSpeech { while(mBufferFilled < num) { int decodedSamples = Audio.FRAME_SIZE; resizeBuffer(mBufferFilled + mAudioBufferSize); - System.arraycopy(mBuffer, mBufferFilled, out, 0, mAudioBufferSize); if(!mLastAlive) Arrays.fill(out, 0); @@ -212,7 +215,7 @@ public class AudioOutputSpeech { } } else { synchronized (mJitterBuffer) { - Speex.jitter_buffer_update_delay(mJitterBuffer, jbp, null); + Speex.jitter_buffer_update_delay(mJitterBuffer, jbp, new int[] { 0 }); } mMissCount++; @@ -281,6 +284,7 @@ public class AudioOutputSpeech { } } + System.arraycopy(out, 0, mBuffer, mBufferFilled, mAudioBufferSize); mBufferFilled += mAudioBufferSize; } @@ -306,6 +310,7 @@ public class AudioOutputSpeech { boolean tmp = mLastAlive; mLastAlive = nextAlive; + return tmp; } diff --git a/src/main/java/com/morlunk/jumble/model/Server.java b/src/main/java/com/morlunk/jumble/model/Server.java index b66b185..d93f54c 100644 --- a/src/main/java/com/morlunk/jumble/model/Server.java +++ b/src/main/java/com/morlunk/jumble/model/Server.java @@ -21,7 +21,7 @@ import android.os.Parcelable; public class Server implements Parcelable { - private int mId; + private long mId; private String mName; private String mHost; private int mPort; @@ -41,7 +41,7 @@ public class Server implements Parcelable { } }; - public Server(int id, String name, String host, int port, String username, String password) { + public Server(long id, String name, String host, int port, String username, String password) { mId = id; mName = name; mHost = host; @@ -57,7 +57,7 @@ public class Server implements Parcelable { @Override public void writeToParcel(Parcel parcel, int i) { - parcel.writeInt(mId); + parcel.writeLong(mId); parcel.writeString(mName); parcel.writeString(mHost); parcel.writeInt(mPort); @@ -66,7 +66,7 @@ public class Server implements Parcelable { } public void readFromParcel(Parcel in) { - mId = in.readInt(); + mId = in.readLong(); mName = in.readString(); mHost = in.readString(); mPort = in.readInt(); @@ -79,11 +79,11 @@ public class Server implements Parcelable { return 0; } - public int getId() { + public long getId() { return mId; } - public void setId(int id) { + public void setId(long id) { mId = id; } diff --git a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java index fc9400c..c0c6a5c 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java @@ -29,21 +29,24 @@ import com.morlunk.jumble.Constants; import com.morlunk.jumble.model.Server; import com.morlunk.jumble.protobuf.Mumble; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpParams; - import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + import java.io.*; import java.net.*; import java.nio.ByteBuffer; import java.security.*; import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; import socks.Socks5Proxy; import socks.SocksSocket; @@ -55,6 +58,14 @@ public class JumbleConnection { public void onConnectionWarning(String warning); } + /** + * Message types that aren't shown in logcat. + * For annoying types like UDPTunnel. + */ + public static final List<JumbleTCPMessageType> UNLOGGED_MESSAGES = Arrays.asList(new JumbleTCPMessageType[] { + JumbleTCPMessageType.UDPTunnel + }); + // Tor connection details public static final String TOR_HOST = "localhost"; public static final int TOR_PORT = 9050; @@ -464,7 +475,8 @@ public class JumbleConnection { } private final void handleTCPMessage(byte[] data, int length, JumbleTCPMessageType messageType) { - Log.v(Constants.TAG, "IN: "+messageType); + if(!UNLOGGED_MESSAGES.contains(messageType)) + Log.v(Constants.TAG, "IN: "+messageType); if(messageType == JumbleTCPMessageType.UDPTunnel) { handleUDPMessage(data); @@ -694,15 +706,10 @@ public class JumbleConnection { handleFatalException(new JumbleConnectionException("Could not resolve host", e, true)); } - HttpParams httpParams = new BasicHttpParams(); - - if(mUseTor) { - Socks5Proxy proxy = new Socks5Proxy(TOR_HOST, TOR_PORT); - proxy.resolveAddrLocally(false); // Tor requirement for SOCKS5 is to let it resolve the host. - SocksSocket proxySocket = new SocksSocket(proxy, mServer.getHost(), mServer.getPort()); - mTCPSocket = (SSLSocket) mSocketFactory.connectSocket(proxySocket, mServer.getHost(), mServer.getPort(), null, 0, httpParams); - } else - mTCPSocket = (SSLSocket) mSocketFactory.connectSocket(null, mServer.getHost(), mServer.getPort(), null, 0, httpParams); + if(mUseTor) + mTCPSocket = mSocketFactory.createTorSocket(mServer.getHost(), mServer.getPort(), TOR_HOST, TOR_PORT); + else + mTCPSocket = mSocketFactory.createSocket(mServer.getHost(), mServer.getPort()); mTCPSocket.startHandshake(); diff --git a/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java b/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java index 55697a5..28344de 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java @@ -17,9 +17,8 @@ package com.morlunk.jumble.net; import java.io.IOException; -import java.net.Proxy; +import java.net.InetSocketAddress; import java.net.Socket; -import java.net.SocketAddress; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -27,66 +26,41 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; +import javax.net.ssl.SSLSocket; -import org.apache.http.conn.ssl.SSLSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.SSLSocketFactory; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import socks.Socks5Proxy; +import socks.SocksSocket; -public class JumbleSSLSocketFactory extends SSLSocketFactory { - - /** - * A trust manager which will accept all remote certificates, regardless of validity. - * FIXME: this is poor cryptographic practice and allows for a potential MitM attack -AC - */ - private static final TrustManager sPermissiveTrustManager = new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - }; - - private SSLContext mSslContext = SSLContext.getInstance(TLS); +public class JumbleSSLSocketFactory { + private SSLSocketFactory mSocketFactory; public JumbleSSLSocketFactory(KeyStore keystore, String keystorePassword) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException, NoSuchProviderException { - super(keystore, keystorePassword, null); // No need for trust store at the moment. FIXME security - - setHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER); // FIXME security - - KeyManager[] keyManagers = null; - - if(keystore != null) { - KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - factory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray() : null); - keyManagers = factory.getKeyManagers(); - } - - mSslContext.init(keyManagers, new TrustManager[] { sPermissiveTrustManager }, new SecureRandom()); + mSocketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, + keystore, + keystorePassword, + null, + new SecureRandom(), + SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); // No need for trust store at the moment. FIXME security } - @Override - public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { - return mSslContext.getSocketFactory().createSocket(socket, host, port, autoClose); + /** + * Creates a new SSLSocket that runs through a SOCKS5 proxy to reach its destination. + */ + public SSLSocket createTorSocket(String host, int port, String proxyHost, int proxyPort) throws IOException { + Socks5Proxy proxy = new Socks5Proxy(proxyHost, proxyPort); + proxy.resolveAddrLocally(false); // Let SOCKS5 proxy resolve host. Useful for Tor. + SocksSocket socksSocket = new SocksSocket(proxy, host, port); + return (SSLSocket) mSocketFactory.createLayeredSocket(socksSocket, proxyHost, proxyPort, new BasicHttpParams()); } - @Override - public Socket createSocket() throws IOException { - return mSslContext.getSocketFactory().createSocket(); + public SSLSocket createSocket(String host, int port) throws IOException { + HttpParams params = new BasicHttpParams(); + Socket socket = mSocketFactory.createSocket(params); + return (SSLSocket) mSocketFactory.connectSocket(socket, new InetSocketAddress(host, port), null, params); } }
\ No newline at end of file |