Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Comminos <andrew@comminos.com>2015-09-26 09:04:24 +0300
committerAndrew Comminos <andrew@comminos.com>2015-09-26 09:04:24 +0300
commit17b4c9f8a787ce2245beb6e830354f27427498b0 (patch)
tree43fb63fc17311230329ab6fecb764c148220663c /src/main/java/com/morlunk
parentc506f116c18a0cd72ae0e9c303390c87a4f74a7d (diff)
Reintroduction of runtime Bluetooth connection support.
Diffstat (limited to 'src/main/java/com/morlunk')
-rw-r--r--src/main/java/com/morlunk/jumble/JumbleService.java91
-rw-r--r--src/main/java/com/morlunk/jumble/audio/BluetoothScoReceiver.java78
-rw-r--r--src/main/java/com/morlunk/jumble/protocol/AudioHandler.java50
3 files changed, 155 insertions, 64 deletions
diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java
index 4046a4f..63e4b3c 100644
--- a/src/main/java/com/morlunk/jumble/JumbleService.java
+++ b/src/main/java/com/morlunk/jumble/JumbleService.java
@@ -34,6 +34,7 @@ import android.os.RemoteException;
import android.util.Log;
import com.morlunk.jumble.audio.AudioOutput;
+import com.morlunk.jumble.audio.BluetoothScoReceiver;
import com.morlunk.jumble.exception.AudioException;
import com.morlunk.jumble.model.Channel;
import com.morlunk.jumble.model.IChannel;
@@ -58,7 +59,7 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
-public class JumbleService extends Service implements JumbleConnection.JumbleConnectionListener, JumbleLogger {
+public class JumbleService extends Service implements JumbleConnection.JumbleConnectionListener, JumbleLogger, BluetoothScoReceiver.Listener {
static {
// Use Spongy Castle for crypto implementation so we can create and manage PKCS #12 (.p12) certificates.
@@ -151,6 +152,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
private int mConnectionState;
private ModelHandler mModelHandler;
private AudioHandler mAudioHandler;
+ private BluetoothScoReceiver mBluetoothReceiver;
private boolean mReconnecting;
@@ -255,10 +257,13 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
.setEncodeListener(mAudioInputListener)
.setTalkingListener(mAudioOutputListener);
mConnectionState = STATE_DISCONNECTED;
+ mBluetoothReceiver = new BluetoothScoReceiver(this, this);
+ registerReceiver(mBluetoothReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
}
@Override
public void onDestroy() {
+ unregisterReceiver(mBluetoothReceiver);
mCallbacks.kill();
super.onDestroy();
}
@@ -397,6 +402,9 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
mModelHandler = null;
mAudioHandler = null;
+ // Halt SCO connection on shutdown.
+ mBluetoothReceiver.stopBluetoothSco();
+
try {
mCallbacks.onDisconnected(e);
} catch (RemoteException re) {
@@ -473,6 +481,29 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
}
/**
+ * Instantiates an audio handler with the current service settings, destroying any previous
+ * handler. Requires synchronization with the server, as the maximum bandwidth and session must
+ * be known.
+ */
+ private void createAudioHandler() throws AudioException {
+ if (BuildConfig.DEBUG && mConnectionState != STATE_CONNECTED) {
+ throw new AssertionError("Attempted to instantiate audio handler when not connected!");
+ }
+
+ if (mAudioHandler != null) {
+ mConnection.removeTCPMessageHandler(mAudioHandler);
+ mConnection.removeUDPMessageHandler(mAudioHandler);
+ mAudioHandler.shutdown();
+ }
+
+ mAudioHandler = mAudioBuilder.initialize(
+ mModelHandler.getUser(mConnection.getSession()),
+ mConnection.getMaxBandwidth(), mConnection.getCodec());
+ mConnection.addTCPMessageHandlers(mAudioHandler);
+ mConnection.addUDPMessageHandlers(mAudioHandler);
+ }
+
+ /**
* Loads all defined settings from the given bundle into the JumbleService.
* Some settings may only take effect after a reconnect.
* @param extras A bundle with settings.
@@ -575,21 +606,38 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
// Reload audio subsystem if initialized
if (mAudioHandler != null && mAudioHandler.isInitialized()) {
- mConnection.removeTCPMessageHandler(mAudioHandler);
- mConnection.removeUDPMessageHandler(mAudioHandler);
- mAudioHandler.shutdown();
-
- mAudioHandler = mAudioBuilder.initialize(
- mModelHandler.getUser(mConnection.getSession()),
- mConnection.getMaxBandwidth(), mConnection.getCodec());
- mConnection.addTCPMessageHandlers(mAudioHandler);
- mConnection.addUDPMessageHandlers(mAudioHandler);
-
+ createAudioHandler();
Log.i(Constants.TAG, "Audio subsystem reloaded after settings change.");
}
return reconnectNeeded;
}
+ @Override
+ public void onBluetoothScoConnected() {
+ // After an SCO connection is established, audio is rerouted to be compatible with SCO.
+ mAudioBuilder.setBluetoothEnabled(true);
+ if (mAudioHandler != null) {
+ try {
+ createAudioHandler();
+ } catch (AudioException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void onBluetoothScoDisconnected() {
+ // Restore audio settings after disconnection.
+ mAudioBuilder.setBluetoothEnabled(false);
+ if (mAudioHandler != null) {
+ try {
+ createAudioHandler();
+ } catch (AudioException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
public class JumbleBinder extends IJumbleService.Stub {
@Override
public int getConnectionState() throws RemoteException {
@@ -715,6 +763,21 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
}
@Override
+ public boolean usingBluetoothSco() throws RemoteException {
+ return mBluetoothReceiver.isBluetoothScoOn();
+ }
+
+ @Override
+ public void enableBluetoothSco() throws RemoteException {
+ mBluetoothReceiver.startBluetoothSco();
+ }
+
+ @Override
+ public void disableBluetoothSco() throws RemoteException {
+ mBluetoothReceiver.stopBluetoothSco();
+ }
+
+ @Override
public boolean isTalking() throws RemoteException {
return mAudioHandler != null && mAudioHandler.isRecording();
}
@@ -739,12 +802,6 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon
}
@Override
- public boolean isBluetoothAvailable() throws RemoteException {
- AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
- return audioManager.isBluetoothScoOn();
- }
-
- @Override
public void joinChannel(int channel) throws RemoteException {
moveUserToChannel(getSession(), channel);
}
diff --git a/src/main/java/com/morlunk/jumble/audio/BluetoothScoReceiver.java b/src/main/java/com/morlunk/jumble/audio/BluetoothScoReceiver.java
new file mode 100644
index 0000000..4577296
--- /dev/null
+++ b/src/main/java/com/morlunk/jumble/audio/BluetoothScoReceiver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Andrew Comminos <andrew@comminos.com>
+ *
+ * This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.morlunk.jumble.audio;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.widget.Toast;
+
+import com.morlunk.jumble.R;
+import com.morlunk.jumble.exception.AudioInitializationException;
+
+/**
+ * Manages the state of Bluetooth SCO.
+ * Created by andrew on 25/09/15.
+ */
+public class BluetoothScoReceiver extends BroadcastReceiver {
+ private final Listener mListener;
+ private final AudioManager mAudioManager;
+ private boolean mBluetoothScoOn;
+
+ public BluetoothScoReceiver(Context context, Listener listener) {
+ mListener = listener;
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ int audioState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR);
+ switch (audioState) {
+ case AudioManager.SCO_AUDIO_STATE_CONNECTED:
+ mBluetoothScoOn = true;
+ mListener.onBluetoothScoConnected();
+ break;
+ case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
+ case AudioManager.SCO_AUDIO_STATE_ERROR:
+ am.stopBluetoothSco();
+ mBluetoothScoOn = false;
+ mListener.onBluetoothScoDisconnected();
+ break;
+ }
+ }
+
+ public void startBluetoothSco() {
+ mAudioManager.startBluetoothSco();
+ }
+
+ public void stopBluetoothSco() {
+ mAudioManager.stopBluetoothSco();
+ mBluetoothScoOn = false;
+ }
+
+ public boolean isBluetoothScoOn() {
+ return mBluetoothScoOn;
+ }
+
+ public interface Listener {
+ void onBluetoothScoConnected();
+ void onBluetoothScoDisconnected();
+ }
+}
diff --git a/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java b/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
index 84b1672..1d5273f 100644
--- a/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
+++ b/src/main/java/com/morlunk/jumble/protocol/AudioHandler.java
@@ -98,41 +98,6 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
private final Object mEncoderLock;
- private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mOutput) {
- int audioState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR);
- switch (audioState) {
- case AudioManager.SCO_AUDIO_STATE_CONNECTED:
- Toast.makeText(mContext, R.string.bluetooth_connected, Toast.LENGTH_LONG).show();
- mOutput.stopPlaying();
- mBluetoothOn = true;
- try {
- mOutput.startPlaying(AudioManager.STREAM_VOICE_CALL);
- } catch (AudioInitializationException e) {
- e.printStackTrace();
- mLogger.logError(e.getLocalizedMessage());
- }
- break;
- case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
- case AudioManager.SCO_AUDIO_STATE_ERROR:
- if (mOutput.isPlaying() && mBluetoothOn)
- Toast.makeText(mContext, R.string.bluetooth_disconnected, Toast.LENGTH_LONG).show();
- mOutput.stopPlaying();
- try {
- mOutput.startPlaying(mAudioStream);
- } catch (AudioInitializationException e) {
- e.printStackTrace();
- mLogger.logError(e.getLocalizedMessage());
- }
- mBluetoothOn = false;
- break;
- }
- }
- }
- };
-
public AudioHandler(Context context, JumbleLogger logger, int audioStream, int audioSource,
int sampleRate, int targetBitrate, int targetFramesPerPacket,
int transmitMode, float vadThreshold, float amplitudeBoost,
@@ -179,8 +144,9 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
setServerMuted(self.isMuted() || self.isLocalMuted() || self.isSuppressed());
if (mTalking && !mMuted)
startRecording();
- // This sticky broadcast will initialize the audio output.
- mContext.registerReceiver(mBluetoothReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
+ // Ensure that if a bluetooth SCO connection is active, we use the VOICE_CALL stream.
+ // This is required by Android for compatibility with SCO.
+ mOutput.startPlaying(mBluetoothOn ? AudioManager.STREAM_VOICE_CALL : mAudioStream);
mInitialized = true;
}
@@ -428,18 +394,8 @@ public class AudioHandler extends JumbleNetworkListener implements AudioInput.Au
mEncoder = null;
}
}
- if(mInitialized) {
- try {
- mContext.unregisterReceiver(mBluetoothReceiver);
- } catch (IllegalArgumentException e) {
- e.printStackTrace(); // Called if the registration failed, and we try and unregister nothing.
- }
- }
mInitialized = false;
mBluetoothOn = false;
- // Restore audio manager mode
- AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- audioManager.stopBluetoothSco();
}