diff options
author | Andrew Comminos <andrew@comminos.com> | 2015-10-25 00:27:15 +0300 |
---|---|---|
committer | Andrew Comminos <andrew@comminos.com> | 2015-10-25 00:27:15 +0300 |
commit | 36b4678a43060075f3a7d0e50ffbeacb57914596 (patch) | |
tree | 60b7968500c65cbc7a25b6336caace32c99439b0 | |
parent | 1e9cdc6e3ef15775e6da7423e2f83f775a688a94 (diff) | |
parent | 28495cdc35bf8a2e680ce3dc8eab228a1bb7565c (diff) |
Merge branch 'bug14/remove-aidl'
26 files changed, 1129 insertions, 939 deletions
diff --git a/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java b/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java index e4f0d68..0776335 100644 --- a/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java +++ b/src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java @@ -21,7 +21,6 @@ import android.content.Intent; import android.os.RemoteException; import android.test.ServiceTestCase; -import com.morlunk.jumble.IJumbleService; import com.morlunk.jumble.JumbleService; import com.morlunk.jumble.model.Server; @@ -45,12 +44,10 @@ public class JumbleServiceTest extends ServiceTestCase<JumbleService> { intent.putExtra(JumbleService.EXTRAS_SERVER, DUMMY_SERVER); startService(intent); JumbleService service = getService(); - IJumbleService binder = service.getBinder(); - assertFalse(binder.isReconnecting()); - assertNull(binder.getConnectionError()); - assertEquals(JumbleService.STATE_DISCONNECTED, binder.getConnectionState()); - assertFalse(binder.isTalking()); - assertEquals(DUMMY_SERVER, binder.getConnectedServer()); + assertFalse(service.isReconnecting()); + assertNull(service.getConnectionError()); + assertEquals(JumbleService.ConnectionState.DISCONNECTED, service.getConnectionState()); + assertEquals(DUMMY_SERVER, service.getConnectedServer()); } } diff --git a/src/main/aidl/com/morlunk/jumble/IJumbleObserver.aidl b/src/main/aidl/com/morlunk/jumble/IJumbleObserver.aidl deleted file mode 100644 index 50e80f3..0000000 --- a/src/main/aidl/com/morlunk/jumble/IJumbleObserver.aidl +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2013 Andrew Comminos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.morlunk.jumble; - -import com.morlunk.jumble.model.IChannel; -import com.morlunk.jumble.model.IUser; -import com.morlunk.jumble.model.IMessage; -import com.morlunk.jumble.util.ParcelableByteArray; -import com.morlunk.jumble.util.JumbleException; - -interface IJumbleObserver { - // Connection - void onConnected(); - void onConnecting(); - void onDisconnected(in JumbleException e); - - // Authentication - void onTLSHandshakeFailed(in ParcelableByteArray cert); - - // Channel - void onChannelAdded(in IChannel channel); - void onChannelStateUpdated(in IChannel channel); - void onChannelRemoved(in IChannel channel); - void onChannelPermissionsUpdated(in IChannel channel); - - // User - void onUserConnected(in IUser user); - void onUserStateUpdated(in IUser user); - void onUserTalkStateUpdated(in IUser user); - void onUserJoinedChannel(in IUser user, in IChannel newChannel, in IChannel oldChannel); - void onUserRemoved(in IUser user, String reason); - void onPermissionDenied(String reason); - - // Logging & Messaging - void onMessageLogged(in IMessage message); - void onLogInfo(String message); - void onLogWarning(String message); - void onLogError(String message); -} diff --git a/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl b/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl deleted file mode 100644 index ea3efd1..0000000 --- a/src/main/aidl/com/morlunk/jumble/IJumbleService.aidl +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2013 Andrew Comminos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.morlunk.jumble; - -import com.morlunk.jumble.model.IUser; -import com.morlunk.jumble.model.IChannel; -import com.morlunk.jumble.model.Server; -import com.morlunk.jumble.model.IMessage; -import com.morlunk.jumble.IJumbleObserver; -import com.morlunk.jumble.util.JumbleException; - -interface IJumbleService { - // Network - int getConnectionState(); - JumbleException getConnectionError(); - boolean isReconnecting(); - void cancelReconnect(); - void disconnect(); - /** - * Gets the TCP latency, in nanoseconds. - */ - long getTCPLatency(); - /** - * Gets the UDP latency, in nanoseconds. - */ - long getUDPLatency(); - int getMaxBandwidth(); - int getCurrentBandwidth(); - - // Server information - int getServerVersion(); - String getServerRelease(); - String getServerOSName(); - String getServerOSVersion(); - - // Session and users - int getSession(); - IUser getSessionUser(); - IChannel getSessionChannel(); - Server getConnectedServer(); - IUser getUser(int id); - IChannel getChannel(int id); - IChannel getRootChannel(); - int getPermissions(); - - // Audio actions and settings - boolean isTalking(); - void setTalkingState(boolean talking); - int getTransmitMode(); - int getCodec(); - - // Bluetooth - boolean usingBluetoothSco(); - void enableBluetoothSco(); - void disableBluetoothSco(); - - // Server actions - void joinChannel(int channel); - void moveUserToChannel(int session, int channel); - void createChannel(int parent, String name, String description, int position, boolean temporary); - void sendAccessTokens(in List tokens); - //void setTexture(byte[] texture); - void requestBanList(); - void requestUserList(); - //void requestACL(int channel); - void requestPermissions(int channel); - void requestComment(int session); - void requestAvatar(int session); - void requestChannelDescription(int channel); - void registerUser(int session); - void kickBanUser(int session, String reason, boolean ban); - IMessage sendUserTextMessage(int session, String message); - IMessage sendChannelTextMessage(int channel, String message, boolean tree); - void setUserComment(int session, String comment); - void setPrioritySpeaker(int session, boolean priority); - void removeChannel(int channel); - //void addChannelLink(int channel, int link); - //void requestChannelPermissions(int channel); - void setMuteDeafState(int session, boolean mute, boolean deaf); - void setSelfMuteDeafState(boolean mute, boolean deaf); - //void announceRecordingState(boolean recording); - - // Observation - void registerObserver(in IJumbleObserver observer); - void unregisterObserver(in IJumbleObserver observer); - - /** - * Reconfigures the JumbleService with the given extras. - * These are the same extras you would pass in for a connect call. This "patches" the service - * only with the new extras specified. - * @return true if the a reconnect is required for changes to take effect. - */ - boolean reconfigure(in Bundle extras); -}
\ No newline at end of file diff --git a/src/main/aidl/com/morlunk/jumble/model/IChannel.aidl b/src/main/aidl/com/morlunk/jumble/model/IChannel.aidl deleted file mode 100644 index e393f0b..0000000 --- a/src/main/aidl/com/morlunk/jumble/model/IChannel.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2013 Andrew Comminos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.morlunk.jumble.model; - -interface IChannel { - int getId(); - int getPosition(); - boolean isTemporary(); - IChannel getParent(); - String getName(); - String getDescription(); - byte[] getDescriptionHash(); - List getUsers(); - List getSubchannels(); - List getLinks(); - int getSubchannelUserCount(); - int getPermissions(); -}
\ No newline at end of file diff --git a/src/main/aidl/com/morlunk/jumble/model/IMessage.aidl b/src/main/aidl/com/morlunk/jumble/model/IMessage.aidl deleted file mode 100644 index 623a94e..0000000 --- a/src/main/aidl/com/morlunk/jumble/model/IMessage.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2013 Andrew Comminos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.morlunk.jumble.model; - -interface IMessage { - int getActor(); - String getActorName(); - List getTargetChannels(); - List getTargetTrees(); - List getTargetUsers(); - String getMessage(); - long getReceivedTime(); -}
\ No newline at end of file diff --git a/src/main/aidl/com/morlunk/jumble/model/IUser.aidl b/src/main/aidl/com/morlunk/jumble/model/IUser.aidl deleted file mode 100644 index aa0f71a..0000000 --- a/src/main/aidl/com/morlunk/jumble/model/IUser.aidl +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2013 Andrew Comminos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.morlunk.jumble.model; - -import com.morlunk.jumble.model.IChannel; -import com.morlunk.jumble.model.TalkState; - -interface IUser { - int getSession(); - IChannel getChannel(); - int getUserId(); - String getName(); - String getComment(); - byte[] getCommentHash(); - byte[] getTexture(); - byte[] getTextureHash(); - String getHash(); - boolean isMuted(); - boolean isDeafened(); - boolean isSuppressed(); - boolean isSelfMuted(); - boolean isSelfDeafened(); - boolean isPrioritySpeaker(); - boolean isRecording(); - boolean isLocalMuted(); - boolean isLocalIgnored(); - void setLocalMuted(boolean localMuted); - void setLocalIgnored(boolean localIgnored); - TalkState getTalkState(); -}
\ No newline at end of file diff --git a/src/main/aidl/com/morlunk/jumble/model/Server.aidl b/src/main/aidl/com/morlunk/jumble/model/Server.aidl deleted file mode 100644 index d39a553..0000000 --- a/src/main/aidl/com/morlunk/jumble/model/Server.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2013 Andrew Comminos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.morlunk.jumble.model; - -parcelable Server; diff --git a/src/main/aidl/com/morlunk/jumble/model/TalkState.aidl b/src/main/aidl/com/morlunk/jumble/model/TalkState.aidl deleted file mode 100644 index 786bc26..0000000 --- a/src/main/aidl/com/morlunk/jumble/model/TalkState.aidl +++ /dev/null @@ -1,4 +0,0 @@ -// TalkState.aidl -package com.morlunk.jumble.model; - -parcelable TalkState;
\ No newline at end of file diff --git a/src/main/aidl/com/morlunk/jumble/util/JumbleException.aidl b/src/main/aidl/com/morlunk/jumble/util/JumbleException.aidl deleted file mode 100644 index d63ffc7..0000000 --- a/src/main/aidl/com/morlunk/jumble/util/JumbleException.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package com.morlunk.jumble.util; - -parcelable JumbleException; diff --git a/src/main/aidl/com/morlunk/jumble/util/ParcelableByteArray.aidl b/src/main/aidl/com/morlunk/jumble/util/ParcelableByteArray.aidl deleted file mode 100644 index 5fb2228..0000000 --- a/src/main/aidl/com/morlunk/jumble/util/ParcelableByteArray.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2013 Andrew Comminos - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.morlunk.jumble.util; - -parcelable ParcelableByteArray; diff --git a/src/main/java/com/morlunk/jumble/IJumbleService.java b/src/main/java/com/morlunk/jumble/IJumbleService.java new file mode 100644 index 0000000..6e149d0 --- /dev/null +++ b/src/main/java/com/morlunk/jumble/IJumbleService.java @@ -0,0 +1,225 @@ +/* + * 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; + +import com.morlunk.jumble.model.IChannel; +import com.morlunk.jumble.model.IUser; +import com.morlunk.jumble.model.Message; +import com.morlunk.jumble.model.Server; +import com.morlunk.jumble.net.JumbleUDPMessageType; +import com.morlunk.jumble.util.IJumbleObserver; +import com.morlunk.jumble.util.JumbleException; + +import java.util.List; + +/** + * A public interface for clients to communicate with a {@link JumbleService}. + * The long-term goal for this class is to migrate of the complexity out of this class into a + * JumbleProtocol class that is owned by a {@link com.morlunk.jumble.net.JumbleConnection}. + * <br><br> + * Calls are not guaranteed to be thread-safe, so only call the binder from the main thread. + * Service state changes related to connection state are only guaranteed to work if isConnected() + * is checked to be true. + * <br><br> + * If not explicitly stated in the method documentation, any call that depends on connection state + * will throw IllegalStateException if disconnected or not synchronized. + */ +public interface IJumbleService { + /** + * Returns the current connection state of the service. + * @return one of {@link JumbleService.ConnectionState}. + */ + JumbleService.ConnectionState getConnectionState(); + + /** + * If the {@link JumbleService} disconnected due to an error, returns that error. + * @return The error causing disconnection. If the last disconnection was successful or a + * connection has yet to be established, returns null. + */ + JumbleException getConnectionError(); + + /** + * Returns the reconnection state of the {@link JumbleService}. + * @return true if the service will attempt to automatically reconnect in the future. + */ + boolean isReconnecting(); + + /** + * Cancels any future reconnection attempts. Does nothing if reconnection is not in progress. + */ + void cancelReconnect(); + + /** + * @return the latency in milliseconds for the TCP connection. + * @throws IllegalStateException if not connected. + */ + long getTCPLatency(); + + /** + * @return the latency in milliseconds for the UDP connection. + * @throws IllegalStateException if not connected. + */ + long getUDPLatency(); + + /** + * @return the maximum bandwidth in bps for audio allowed by the server, or -1 if not set. + * @throws IllegalStateException if not synchronized. + */ + int getMaxBandwidth(); + + /** + * @return the current bandwidth in bps for audio sent to the server, or a negative integer + * if unknown (prior to connection or after disconnection). + * @throws IllegalStateException if not synchronized. + */ + int getCurrentBandwidth(); + + /** + * Returns the protocol version returned by the server in the format 0xAABBCC, where AA + * indicates the major version, BB indicates the minor version, and CC indicates the patch + * version. This is the same formatting used by the Mumble protocol in big-endian format. + * @return the current bandwidth in bps for audio sent to the server, or a negative integer + * if unknown (prior to connection or after disconnection). + * @throws IllegalStateException if not synchronized. + */ + int getServerVersion(); + + /** + * @return a user-readable string with the server's Mumble release info. + * @throws IllegalStateException if not synchronized. + */ + String getServerRelease(); + + /** + * @return a user-readable string with the server's OS name. + * @throws IllegalStateException if not connected. + */ + String getServerOSName(); + + /** + * @return a user-readable string with the server's OS version. + * @throws IllegalStateException if not synchronized. + */ + String getServerOSVersion(); + + /** + * Returns the current user's session. Set during server synchronization. + * @return an integer identifying the current user's connection. + * @throws IllegalStateException if not synchronized. + */ + int getSession(); + + /** + * Returns the current user. Set during server synchronization. + * @return the {@link IUser} representing the current user. + * @throws IllegalStateException if not synchronized. + */ + IUser getSessionUser(); + + /** + * Returns the user's current channel. + * @return the {@link IChannel} representing the user's current channel. + * @throws IllegalStateException if not synchronized. + */ + IChannel getSessionChannel(); + + /** + * @return the server that Jumble is currently connected to (or attempted connection to). + */ + Server getConnectedServer(); + + /** + * Retrieves the user with the given session ID. + * @param session An integer ID identifying a user's session. See {@link IUser#getSession()}. + * @return A user with the given session, or null if not found. + * @throws IllegalStateException if not synchronized. + */ + IUser getUser(int session); + + /** + * Retrieves the channel with the given ID. + * @param id An integer ID identifying a channel. See {@link IChannel#getId()}. + * @return A channel with the given session, or null if not found. + * @throws IllegalStateException if not synchronized. + */ + IChannel getChannel(int id); + + /** + * @return the root channel of the server. + * @throws IllegalStateException if not synchronized. + */ + IChannel getRootChannel(); + + int getPermissions(); + + int getTransmitMode(); + + JumbleUDPMessageType getCodec(); + + boolean usingBluetoothSco(); + + void enableBluetoothSco(); + + void disableBluetoothSco(); + + boolean isTalking(); + + void setTalkingState(boolean talking); + + void joinChannel(int channel); + + void moveUserToChannel(int session, int channel); + + void createChannel(int parent, String name, String description, int position, boolean temporary); + + void sendAccessTokens(List<String> tokens); + + void requestBanList(); + + void requestUserList(); + + void requestPermissions(int channel); + + void requestComment(int session); + + void requestAvatar(int session); + + void requestChannelDescription(int channel); + + void registerUser(int session); + + void kickBanUser(int session, String reason, boolean ban); + + Message sendUserTextMessage(int session, String message); + + Message sendChannelTextMessage(int channel, String message, boolean tree); + + void setUserComment(int session, String comment); + + void setPrioritySpeaker(int session, boolean priority); + + void removeChannel(int channel); + + void setMuteDeafState(int session, boolean mute, boolean deaf); + + void setSelfMuteDeafState(boolean mute, boolean deaf); + + void registerObserver(IJumbleObserver observer); + + void unregisterObserver(IJumbleObserver observer); +} diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java index 63e4b3c..e9fd0c1 100644 --- a/src/main/java/com/morlunk/jumble/JumbleService.java +++ b/src/main/java/com/morlunk/jumble/JumbleService.java @@ -25,17 +25,19 @@ import android.content.IntentFilter; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; -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.exception.NotConnectedException; +import com.morlunk.jumble.exception.NotSynchronizedException; import com.morlunk.jumble.model.Channel; import com.morlunk.jumble.model.IChannel; import com.morlunk.jumble.model.IUser; @@ -44,6 +46,8 @@ import com.morlunk.jumble.model.Server; import com.morlunk.jumble.model.TalkState; import com.morlunk.jumble.model.User; import com.morlunk.jumble.net.JumbleConnection; +import com.morlunk.jumble.net.JumbleUDPMessageType; +import com.morlunk.jumble.util.IJumbleObserver; import com.morlunk.jumble.util.JumbleException; import com.morlunk.jumble.net.JumbleTCPMessageType; import com.morlunk.jumble.protobuf.Mumble; @@ -51,15 +55,13 @@ import com.morlunk.jumble.protocol.AudioHandler; import com.morlunk.jumble.protocol.ModelHandler; import com.morlunk.jumble.util.JumbleCallbacks; import com.morlunk.jumble.util.JumbleLogger; -import com.morlunk.jumble.util.ParcelableByteArray; import java.security.Security; -import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; -public class JumbleService extends Service implements JumbleConnection.JumbleConnectionListener, JumbleLogger, BluetoothScoReceiver.Listener { +public class JumbleService extends Service implements IJumbleService, JumbleConnection.JumbleConnectionListener, JumbleLogger, BluetoothScoReceiver.Listener { static { // Use Spongy Castle for crypto implementation so we can create and manage PKCS #12 (.p12) certificates. @@ -67,27 +69,6 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } /** - * The default state of Jumble, before connection to a server and after graceful/expected - * disconnection from a server. - */ - public static final int STATE_DISCONNECTED = 0; - /** - * A connection to the server is currently in progress. - */ - public static final int STATE_CONNECTING = 1; - /** - * Jumble has received all data necessary for normal protocol communication with the server. - */ - public static final int STATE_CONNECTED = 2; - /** - * The connection was lost due to either a kick/ban or socket I/O error. - * Jumble can be reconnecting in this state. - * @see IJumbleService#isReconnecting() - * @see IJumbleService#cancelReconnect() - */ - public static final int STATE_CONNECTION_LOST = 3; - - /** * An action to immediately connect to a given Mumble server. * Requires that {@link #EXTRAS_SERVER} is provided. */ @@ -146,10 +127,9 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon private PowerManager.WakeLock mWakeLock; private Handler mHandler; private JumbleCallbacks mCallbacks; - private IJumbleService.Stub mBinder = new JumbleBinder(); private JumbleConnection mConnection; - private int mConnectionState; + private ConnectionState mConnectionState; private ModelHandler mModelHandler; private AudioHandler mAudioHandler; private BluetoothScoReceiver mBluetoothReceiver; @@ -189,14 +169,16 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon mHandler.post(new Runnable() { @Override public void run() { - if(!isConnected()) return; - final User currentUser = mModelHandler.getUser(mConnection.getSession()); - if(currentUser == null) return; - - currentUser.setTalkState(state); try { + if (!isSynchronized()) + throw new NotSynchronizedException(); + + final User currentUser = mModelHandler.getUser(mConnection.getSession()); + if (currentUser == null) return; + + currentUser.setTalkState(state); mCallbacks.onUserTalkStateUpdated(currentUser); - } catch (RemoteException e) { + } catch (NotSynchronizedException e) { e.printStackTrace(); } } @@ -207,11 +189,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon private AudioOutput.AudioOutputListener mAudioOutputListener = new AudioOutput.AudioOutputListener() { @Override public void onUserTalkStateUpdated(final User user) { - try { - mCallbacks.onUserTalkStateUpdated(user); - } catch (RemoteException e) { - e.printStackTrace(); - } + mCallbacks.onUserTalkStateUpdated(user); } @Override @@ -234,7 +212,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon if (ACTION_CONNECT.equals(intent.getAction())) { if (extras == null || !extras.containsKey(EXTRAS_SERVER)) { - // Ensure that we have been provided all required attributes. + // Ensure that we have been provided all required attributes.``` throw new RuntimeException(ACTION_CONNECT + " requires a server provided in extras."); } connect(); @@ -256,7 +234,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon .setLogger(this) .setEncodeListener(mAudioInputListener) .setTalkingListener(mAudioOutputListener); - mConnectionState = STATE_DISCONNECTED; + mConnectionState = ConnectionState.DISCONNECTED; mBluetoothReceiver = new BluetoothScoReceiver(this, this); registerReceiver(mBluetoothReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED)); } @@ -264,22 +242,17 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon @Override public void onDestroy() { unregisterReceiver(mBluetoothReceiver); - mCallbacks.kill(); super.onDestroy(); } public IBinder onBind(Intent intent) { - return mBinder; - } - - public IJumbleService getBinder() { - return mBinder; + return new JumbleBinder(this); } - public void connect() { + protected void connect() { try { setReconnecting(false); - mConnectionState = STATE_DISCONNECTED; + mConnectionState = ConnectionState.DISCONNECTED; mConnection = new JumbleConnection(this); mConnection.setForceTCP(mForceTcp); @@ -291,22 +264,14 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon mLocalMuteHistory, mLocalIgnoreHistory); mConnection.addTCPMessageHandlers(mModelHandler); - mConnectionState = STATE_CONNECTING; + mConnectionState = ConnectionState.CONNECTING; - try { - mCallbacks.onConnecting(); - } catch (RemoteException e) { - e.printStackTrace(); - } + mCallbacks.onConnecting(); mConnection.connect(mServer.getHost(), mServer.getPort()); } catch (JumbleException e) { e.printStackTrace(); - try { - mCallbacks.onDisconnected(e); - } catch (RemoteException e1) { - e1.printStackTrace(); - } + mCallbacks.onDisconnected(e); } } @@ -314,10 +279,18 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon mConnection.disconnect(); } - public boolean isConnected() { + public boolean isConnectionEstablished() { return mConnection != null && mConnection.isConnected(); } + /** + * @return true if Jumble has received the ServerSync message, indicating synchronization with + * the server's model and settings. This is the main state of the service. + */ + public boolean isSynchronized() { + return mConnection != null && mConnection.isSynchronized(); + } + @Override public void onConnectionEstablished() { // Send version information and authenticate. @@ -342,7 +315,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon @Override public void onConnectionSynchronized() { - mConnectionState = STATE_CONNECTED; + mConnectionState = ConnectionState.CONNECTED; Log.v(Constants.TAG, "Connected"); mWakeLock.acquire(); @@ -356,25 +329,16 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } catch (AudioException e) { e.printStackTrace(); onConnectionWarning(e.getMessage()); + } catch (NotSynchronizedException e) { + throw new RuntimeException("Connection should be synchronized in callback for synchronization!", e); } - try { - mCallbacks.onConnected(); - } catch (RemoteException e) { - e.printStackTrace(); - } + mCallbacks.onConnected(); } @Override public void onConnectionHandshakeFailed(X509Certificate[] chain) { - try { - final ParcelableByteArray encodedCert = new ParcelableByteArray(chain[0].getEncoded()); - mCallbacks.onTLSHandshakeFailed(encodedCert); - } catch (CertificateEncodingException e) { - e.printStackTrace(); - } catch (RemoteException e) { - e.printStackTrace(); - } + mCallbacks.onTLSHandshakeFailed(chain); } @Override @@ -382,13 +346,13 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon if (e != null) { Log.e(Constants.TAG, "Error: " + e.getMessage() + " (reason: " + e.getReason().name() + ")"); - mConnectionState = STATE_CONNECTION_LOST; + mConnectionState = ConnectionState.CONNECTION_LOST; setReconnecting(mAutoReconnect && e.getReason() == JumbleException.JumbleDisconnectReason.CONNECTION_ERROR); } else { Log.v(Constants.TAG, "Disconnected"); - mConnectionState = STATE_DISCONNECTED; + mConnectionState = ConnectionState.DISCONNECTED; } if(mWakeLock.isHeld()) { @@ -405,11 +369,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon // Halt SCO connection on shutdown. mBluetoothReceiver.stopBluetoothSco(); - try { - mCallbacks.onDisconnected(e); - } catch (RemoteException re) { - re.printStackTrace(); - } + mCallbacks.onDisconnected(e); } @Override @@ -421,32 +381,23 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon public void logInfo(String message) { if (mConnection == null || !mConnection.isSynchronized()) return; // don't log info prior to synchronization - try { - mCallbacks.onLogInfo(message); - } catch (RemoteException e) { - e.printStackTrace(); - } + mCallbacks.onLogInfo(message); } @Override public void logWarning(String message) { - try { - mCallbacks.onLogWarning(message); - } catch (RemoteException e) { - e.printStackTrace(); - } + mCallbacks.onLogWarning(message); } @Override public void logError(String message) { - try { - mCallbacks.onLogError(message); - } catch (RemoteException e) { - e.printStackTrace(); - } + mCallbacks.onLogError(message); } - private void setReconnecting(boolean reconnecting) { + public void setReconnecting(boolean reconnecting) { + if (mReconnecting == reconnecting) + return; + mReconnecting = reconnecting; if (reconnecting) { ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); @@ -486,7 +437,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon * be known. */ private void createAudioHandler() throws AudioException { - if (BuildConfig.DEBUG && mConnectionState != STATE_CONNECTED) { + if (BuildConfig.DEBUG && mConnectionState != ConnectionState.CONNECTED) { throw new AssertionError("Attempted to instantiate audio handler when not connected!"); } @@ -496,11 +447,15 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon mAudioHandler.shutdown(); } - mAudioHandler = mAudioBuilder.initialize( - mModelHandler.getUser(mConnection.getSession()), - mConnection.getMaxBandwidth(), mConnection.getCodec()); - mConnection.addTCPMessageHandlers(mAudioHandler); - mConnection.addUDPMessageHandlers(mAudioHandler); + try { + mAudioHandler = mAudioBuilder.initialize( + mModelHandler.getUser(mConnection.getSession()), + mConnection.getMaxBandwidth(), mConnection.getCodec()); + mConnection.addTCPMessageHandlers(mAudioHandler); + mConnection.addUDPMessageHandlers(mAudioHandler); + } catch (NotSynchronizedException e) { + throw new RuntimeException("Attempted to create audio handler when not synchronized!"); + } } /** @@ -510,7 +465,7 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon * @return true if a reconnect is required for changes to take effect. * @see com.morlunk.jumble.JumbleService */ - private boolean configureExtras(Bundle extras) throws AudioException { + public boolean configureExtras(Bundle extras) throws AudioException { boolean reconnectNeeded = false; if (extras.containsKey(EXTRAS_SERVER)) { mServer = extras.getParcelable(EXTRAS_SERVER); @@ -638,342 +593,495 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon } } - public class JumbleBinder extends IJumbleService.Stub { - @Override - public int getConnectionState() throws RemoteException { - return mConnectionState; - } + /** + * Exposes the current connection. The current connection is set once an attempt to connect to + * a server is made, and remains set until a subsequent connection. It remains available + * after disconnection to provide information regarding the terminated connection. + * @return The active {@link JumbleConnection}. + */ + public JumbleConnection getConnection() { + return mConnection; + } - @Override - public JumbleException getConnectionError() throws RemoteException { - return mConnection != null ? mConnection.getError() : null; - } + /** + * Returnes the current {@link AudioHandler}. An AudioHandler is instantiated upon connection + * to a server, and destroyed upon disconnection. + * @return the active AudioHandler, or null if there is no active connection. + */ + private AudioHandler getAudioHandler() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); + if (mAudioHandler == null && mConnectionState == ConnectionState.CONNECTED) + throw new RuntimeException("Audio handler should always be instantiated while connected!"); + return mAudioHandler; + } - @Override - public boolean isReconnecting() throws RemoteException { - return mReconnecting; - } + /** + * Returns the current {@link ModelHandler}, containing the channel tree. A model handler is + * valid for the lifetime of a connection. + * @return the active ModelHandler, or null if there is no active connection. + */ + private ModelHandler getModelHandler() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); + if (mModelHandler == null && mConnectionState == ConnectionState.CONNECTED) + throw new RuntimeException("Model handler should always be instantiated while connected!"); + return mModelHandler; + } - @Override - public void cancelReconnect() throws RemoteException { - setReconnecting(false); - } + /** + * Returns the bluetooth service provider, established after synchronization. + * @return The {@link BluetoothScoReceiver} attached to this service. + */ + private BluetoothScoReceiver getBluetoothReceiver() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); + return mBluetoothReceiver; + } - @Override - public void disconnect() throws RemoteException { - JumbleService.this.disconnect(); - } + @Override + public JumbleService.ConnectionState getConnectionState() { + return mConnectionState; + } - @Override - public long getTCPLatency() throws RemoteException { - return mConnection.getTCPLatency(); - } + @Override + public JumbleException getConnectionError() { + JumbleConnection connection = getConnection(); + return connection != null ? connection.getError() : null; + } - @Override - public long getUDPLatency() throws RemoteException { - return mConnection.getUDPLatency(); - } + @Override + public boolean isReconnecting() { + return mReconnecting; + } - @Override - public int getMaxBandwidth() throws RemoteException { - return mConnection.getMaxBandwidth(); - } + @Override + public void cancelReconnect() { + setReconnecting(false); + } - @Override - public int getCurrentBandwidth() throws RemoteException { - return mAudioHandler.getCurrentBandwidth(); + @Override + public long getTCPLatency() { + try { + return getConnection().getTCPLatency(); + } catch (NotConnectedException e) { + throw new IllegalStateException(e); } + } - @Override - public int getServerVersion() throws RemoteException { - return mConnection.getServerVersion(); + @Override + public long getUDPLatency() { + try { + return getConnection().getUDPLatency(); + } catch (NotConnectedException e) { + throw new IllegalStateException(e); } + } - @Override - public String getServerRelease() throws RemoteException { - return mConnection.getServerRelease(); + @Override + public int getMaxBandwidth() { + try { + return getConnection().getMaxBandwidth(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public String getServerOSName() throws RemoteException { - return mConnection.getServerOSName(); + @Override + public int getCurrentBandwidth() { + try { + return getAudioHandler().getCurrentBandwidth(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public String getServerOSVersion() throws RemoteException { - return mConnection.getServerOSVersion(); + @Override + public int getServerVersion() { + try { + return getConnection().getServerVersion(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public int getSession() throws RemoteException { - return mConnection != null ? mConnection.getSession() : -1; + @Override + public String getServerRelease() { + try { + return getConnection().getServerRelease(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public IUser getSessionUser() throws RemoteException { - return mModelHandler != null ? mModelHandler.getUser(getSession()) : null; + @Override + public String getServerOSName() { + try { + return getConnection().getServerOSName(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public IChannel getSessionChannel() throws RemoteException { - IUser user = getSessionUser(); - if (user != null) { - return user.getChannel(); - } - return null; + @Override + public String getServerOSVersion() { + try { + return getConnection().getServerOSVersion(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public Server getConnectedServer() throws RemoteException { - return mServer; + @Override + public int getSession() { + try { + return getConnection().getSession(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public IUser getUser(int id) throws RemoteException { - if (mModelHandler != null) - return mModelHandler.getUser(id); - return null; + @Override + public IUser getSessionUser() { + try { + return getModelHandler().getUser(getSession()); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public IChannel getChannel(int id) throws RemoteException { - if (mModelHandler != null) - return mModelHandler.getChannel(id); - return null; + @Override + public IChannel getSessionChannel() { + IUser user = getSessionUser(); + if (user != null) + return user.getChannel(); + throw new IllegalStateException("Session user should be set post-synchronization!"); + } + + @Override + public Server getConnectedServer() { + return mServer; + } + + @Override + public IUser getUser(int session) { + try { + return getModelHandler().getUser(session); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public IChannel getRootChannel() throws RemoteException { - return getChannel(0); + @Override + public IChannel getChannel(int id) { + try { + return getModelHandler().getChannel(id); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public int getPermissions() throws RemoteException { - return mModelHandler != null ? mModelHandler.getPermissions() : 0; + @Override + public IChannel getRootChannel() { + return getChannel(0); + } + + @Override + public int getPermissions() { + try { + return getModelHandler().getPermissions(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public int getTransmitMode() throws RemoteException { - return mAudioHandler.getTransmitMode(); + @Override + public int getTransmitMode() { + try { + return getAudioHandler().getTransmitMode(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public int getCodec() throws RemoteException { - return mConnection.getCodec().ordinal(); // FIXME: ordinal is bad, make enum method + @Override + public JumbleUDPMessageType getCodec() { + try { + return getConnection().getCodec(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public boolean usingBluetoothSco() throws RemoteException { - return mBluetoothReceiver.isBluetoothScoOn(); + @Override + public boolean usingBluetoothSco() { + try { + return getBluetoothReceiver().isBluetoothScoOn(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public void enableBluetoothSco() throws RemoteException { - mBluetoothReceiver.startBluetoothSco(); + @Override + public void enableBluetoothSco() { + try { + getBluetoothReceiver().startBluetoothSco(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public void disableBluetoothSco() throws RemoteException { - mBluetoothReceiver.stopBluetoothSco(); + @Override + public void disableBluetoothSco() { + try { + getBluetoothReceiver().stopBluetoothSco(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public boolean isTalking() throws RemoteException { - return mAudioHandler != null && mAudioHandler.isRecording(); + @Override + public boolean isTalking() { + try { + return getAudioHandler().isRecording(); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public void setTalkingState(boolean talking) throws RemoteException { - if(getSessionUser() != null && - (getSessionUser().isSelfMuted() || getSessionUser().isMuted())) { - return; - } + @Override + public void setTalkingState(boolean talking) { + if (getSessionUser().isSelfMuted() || getSessionUser().isMuted()) + return; - if (mAudioHandler.getTransmitMode() != Constants.TRANSMIT_PUSH_TO_TALK) { + try { + if (getAudioHandler().getTransmitMode() != Constants.TRANSMIT_PUSH_TO_TALK) { Log.w(Constants.TAG, "Attempted to set talking state when not using PTT"); return; } try { - mAudioHandler.setTalking(talking); + getAudioHandler().setTalking(talking); } catch (AudioException e) { logError(e.getMessage()); } + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public void joinChannel(int channel) throws RemoteException { - moveUserToChannel(getSession(), channel); - } + @Override + public void joinChannel(int channel) { + moveUserToChannel(getSession(), channel); + } - @Override - public void moveUserToChannel(int session, int channel) throws RemoteException { - Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); - usb.setSession(session); - usb.setChannelId(channel); - mConnection.sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); - } + @Override + public void moveUserToChannel(int session, int channel) { + Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); + usb.setSession(session); + usb.setChannelId(channel); + getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); + } - @Override - public void createChannel(int parent, String name, String description, int position, boolean temporary) throws RemoteException { - Mumble.ChannelState.Builder csb = Mumble.ChannelState.newBuilder(); - csb.setParent(parent); - csb.setName(name); - csb.setDescription(description); - csb.setPosition(position); - csb.setTemporary(temporary); - mConnection.sendTCPMessage(csb.build(), JumbleTCPMessageType.ChannelState); - } + @Override + public void createChannel(int parent, String name, String description, int position, boolean temporary) { + Mumble.ChannelState.Builder csb = Mumble.ChannelState.newBuilder(); + csb.setParent(parent); + csb.setName(name); + csb.setDescription(description); + csb.setPosition(position); + csb.setTemporary(temporary); + getConnection().sendTCPMessage(csb.build(), JumbleTCPMessageType.ChannelState); + } - @Override - public void sendAccessTokens(List tokens) throws RemoteException { - mConnection.sendAccessTokens(tokens); - } + @Override + public void sendAccessTokens(final List<String> tokens) { + getConnection().sendAccessTokens(tokens); + } - @Override - public void requestBanList() throws RemoteException { - throw new UnsupportedOperationException("Not yet implemented"); // TODO - } + @Override + public void requestBanList() { + throw new UnsupportedOperationException("Not yet implemented"); // TODO + } - @Override - public void requestUserList() throws RemoteException { - throw new UnsupportedOperationException("Not yet implemented"); // TODO - } + @Override + public void requestUserList() { + throw new UnsupportedOperationException("Not yet implemented"); // TODO + } - @Override - public void requestPermissions(int channel) throws RemoteException { - Mumble.PermissionQuery.Builder pqb = Mumble.PermissionQuery.newBuilder(); - pqb.setChannelId(channel); - mConnection.sendTCPMessage(pqb.build(), JumbleTCPMessageType.PermissionQuery); - } + @Override + public void requestPermissions(int channel) { + Mumble.PermissionQuery.Builder pqb = Mumble.PermissionQuery.newBuilder(); + pqb.setChannelId(channel); + getConnection().sendTCPMessage(pqb.build(), JumbleTCPMessageType.PermissionQuery); + } - @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 requestComment(int session) { + Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder(); + rbb.addSessionComment(session); + getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob); + } - @Override - public void requestAvatar(int session) throws RemoteException { - Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder(); - rbb.addSessionTexture(session); - mConnection.sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob); - } + @Override + public void requestAvatar(int session) { + Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder(); + rbb.addSessionTexture(session); + getConnection().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 requestChannelDescription(int channel) { + Mumble.RequestBlob.Builder rbb = Mumble.RequestBlob.newBuilder(); + rbb.addChannelDescription(channel); + getConnection().sendTCPMessage(rbb.build(), JumbleTCPMessageType.RequestBlob); + } - @Override - public void registerUser(int session) throws RemoteException { - Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); - usb.setSession(session); - usb.setUserId(0); - mConnection.sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); - } + @Override + public void registerUser(int session) { + Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); + usb.setSession(session); + usb.setUserId(0); + getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); + } - @Override - public void kickBanUser(int session, String reason, boolean ban) throws RemoteException { - Mumble.UserRemove.Builder urb = Mumble.UserRemove.newBuilder(); - urb.setSession(session); - urb.setReason(reason); - urb.setBan(ban); - mConnection.sendTCPMessage(urb.build(), JumbleTCPMessageType.UserRemove); - } + @Override + public void kickBanUser(int session, String reason, boolean ban) { + Mumble.UserRemove.Builder urb = Mumble.UserRemove.newBuilder(); + urb.setSession(session); + urb.setReason(reason); + urb.setBan(ban); + getConnection().sendTCPMessage(urb.build(), JumbleTCPMessageType.UserRemove); + } + + @Override + public Message sendUserTextMessage(int session, String message) { + try { + if (!isSynchronized()) + throw new NotSynchronizedException(); - @Override - public Message sendUserTextMessage(int session, String message) throws RemoteException { Mumble.TextMessage.Builder tmb = Mumble.TextMessage.newBuilder(); tmb.addSession(session); tmb.setMessage(message); - mConnection.sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage); + getConnection().sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage); - User self = mModelHandler.getUser(getSession()); - User user = mModelHandler.getUser(session); + User self = getModelHandler().getUser(getSession()); + User user = getModelHandler().getUser(session); List<User> users = new ArrayList<User>(1); users.add(user); return new Message(getSession(), self.getName(), new ArrayList<Channel>(0), new ArrayList<Channel>(0), users, message); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } + + @Override + public Message sendChannelTextMessage(int channel, String message, boolean tree) { + try { + if (!isSynchronized()) + throw new NotSynchronizedException(); - @Override - public Message sendChannelTextMessage(int channel, String message, boolean tree) throws RemoteException { Mumble.TextMessage.Builder tmb = Mumble.TextMessage.newBuilder(); - if(tree) tmb.addTreeId(channel); + if (tree) tmb.addTreeId(channel); else tmb.addChannelId(channel); tmb.setMessage(message); - mConnection.sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage); + getConnection().sendTCPMessage(tmb.build(), JumbleTCPMessageType.TextMessage); - User self = mModelHandler.getUser(getSession()); - Channel targetChannel = mModelHandler.getChannel(channel); + User self = getModelHandler().getUser(getSession()); + Channel targetChannel = getModelHandler().getChannel(channel); List<Channel> targetChannels = new ArrayList<Channel>(); targetChannels.add(targetChannel); return new Message(getSession(), self.getName(), targetChannels, tree ? targetChannels : new ArrayList<Channel>(0), new ArrayList<User>(0), message); + } catch (NotSynchronizedException e) { + throw new IllegalStateException(e); } + } - @Override - public void setUserComment(int session, String comment) throws RemoteException { - Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); - usb.setSession(session); - usb.setComment(comment); - mConnection.sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); - } + @Override + public void setUserComment(int session, String comment) { + Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); + usb.setSession(session); + usb.setComment(comment); + getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); + } - @Override - public void setPrioritySpeaker(int session, boolean priority) throws RemoteException { - Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); - usb.setSession(session); - usb.setPrioritySpeaker(priority); - mConnection.sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); - } + @Override + public void setPrioritySpeaker(int session, boolean priority) { + Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); + usb.setSession(session); + usb.setPrioritySpeaker(priority); + getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); + } - @Override - public void removeChannel(int channel) throws RemoteException { - Mumble.ChannelRemove.Builder crb = Mumble.ChannelRemove.newBuilder(); - crb.setChannelId(channel); - mConnection.sendTCPMessage(crb.build(), JumbleTCPMessageType.ChannelRemove); - } + @Override + public void removeChannel(int channel) { + Mumble.ChannelRemove.Builder crb = Mumble.ChannelRemove.newBuilder(); + crb.setChannelId(channel); + getConnection().sendTCPMessage(crb.build(), JumbleTCPMessageType.ChannelRemove); + } - @Override - public void setMuteDeafState(int session, boolean mute, boolean deaf) throws RemoteException { - Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); - usb.setSession(session); - usb.setMute(mute); - usb.setDeaf(deaf); - if (!mute) usb.setSuppress(false); - mConnection.sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); - } + @Override + public void setMuteDeafState(int session, boolean mute, boolean deaf) { + Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); + usb.setSession(session); + usb.setMute(mute); + usb.setDeaf(deaf); + if (!mute) usb.setSuppress(false); + getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); + } - @Override - public void setSelfMuteDeafState(boolean mute, boolean deaf) throws RemoteException { - Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); - usb.setSelfMute(mute); - usb.setSelfDeaf(deaf); - mConnection.sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); - } + @Override + public void setSelfMuteDeafState(boolean mute, boolean deaf) { + Mumble.UserState.Builder usb = Mumble.UserState.newBuilder(); + usb.setSelfMute(mute); + usb.setSelfDeaf(deaf); + getConnection().sendTCPMessage(usb.build(), JumbleTCPMessageType.UserState); + } - @Override - public void registerObserver(IJumbleObserver observer) throws RemoteException { - mCallbacks.registerObserver(observer); - } + public void registerObserver(IJumbleObserver observer) { + mCallbacks.registerObserver(observer); + } - @Override - public void unregisterObserver(IJumbleObserver observer) throws RemoteException { - mCallbacks.unregisterObserver(observer); + public void unregisterObserver(IJumbleObserver observer) { + mCallbacks.unregisterObserver(observer); + } + + + /** + * The current connection state of the service. + */ + public enum ConnectionState { + /** + * The default state of Jumble, before connection to a server and after graceful/expected + * disconnection from a server. + */ + DISCONNECTED, + /** + * A connection to the server is currently in progress. + */ + CONNECTING, + /** + * Jumble has received all data necessary for normal protocol communication with the server. + */ + CONNECTED, + /** + * The connection was lost due to either a kick/ban or socket I/O error. + * Jumble may be reconnecting in this state. + * @see #isReconnecting() + * @see #cancelReconnect() + */ + CONNECTION_LOST + } + + public static class JumbleBinder extends Binder { + private final IJumbleService mService; + + private JumbleBinder(IJumbleService service) { + mService = service; } - @Override - public boolean reconfigure(Bundle extras) throws RemoteException { - try { - return configureExtras(extras); - } catch (AudioException e) { - e.printStackTrace(); - // TODO - return true; - } + public IJumbleService getService() { + return mService; } } } diff --git a/src/main/java/com/morlunk/jumble/exception/NotConnectedException.java b/src/main/java/com/morlunk/jumble/exception/NotConnectedException.java new file mode 100644 index 0000000..c81db73 --- /dev/null +++ b/src/main/java/com/morlunk/jumble/exception/NotConnectedException.java @@ -0,0 +1,32 @@ +/* + * 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.exception; + +/** + * Thrown when a Jumble connection has not yet been established. + * Created by andrew on 24/10/15. + */ +public class NotConnectedException extends Exception { + public NotConnectedException() { + super("Not yet connected"); + } + + public NotConnectedException(String message) { + super(message); + } +} diff --git a/src/main/java/com/morlunk/jumble/exception/NotSynchronizedException.java b/src/main/java/com/morlunk/jumble/exception/NotSynchronizedException.java new file mode 100644 index 0000000..c65fd15 --- /dev/null +++ b/src/main/java/com/morlunk/jumble/exception/NotSynchronizedException.java @@ -0,0 +1,32 @@ +/* + * 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.exception; + +/** + * Called when Jumble has not yet received the ServerSync message from the server. + * Created by andrew on 24/10/15. + */ +public class NotSynchronizedException extends Exception { + public NotSynchronizedException() { + super("Not yet synchronized"); + } + + public NotSynchronizedException(String message) { + super(message); + } +} diff --git a/src/main/java/com/morlunk/jumble/model/Channel.java b/src/main/java/com/morlunk/jumble/model/Channel.java index 7edc95f..142754f 100644 --- a/src/main/java/com/morlunk/jumble/model/Channel.java +++ b/src/main/java/com/morlunk/jumble/model/Channel.java @@ -21,7 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public final class Channel extends IChannel.Stub implements Comparable<Channel> { +public final class Channel implements IChannel, Comparable<Channel> { private int mId; private int mPosition; private int mLevel; @@ -68,10 +68,12 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> mUsers.remove(user); } + @Override public List<User> getUsers() { return Collections.unmodifiableList(mUsers); } + @Override public int getId() { return mId; } @@ -80,6 +82,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> this.mId = mId; } + @Override public int getPosition() { return mPosition; } @@ -88,6 +91,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> this.mPosition = mPosition; } + @Override public boolean isTemporary() { return mTemporary; } @@ -96,6 +100,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> this.mTemporary = mTemporary; } + @Override public Channel getParent() { return mParent; } @@ -104,6 +109,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> this.mParent = mParent; } + @Override public String getName() { return mName; } @@ -112,6 +118,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> this.mName = mName; } + @Override public String getDescription() { return mDescription; } @@ -120,6 +127,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> this.mDescription = mDescription; } + @Override public byte[] getDescriptionHash() { return mDescriptionHash; } @@ -128,6 +136,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> this.mDescriptionHash = mDescriptionHash; } + @Override public List<Channel> getSubchannels() { return Collections.unmodifiableList(mSubchannels); } @@ -147,6 +156,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> mSubchannels.remove(channel); } + @Override public List<Channel> getLinks() { return Collections.unmodifiableList(mLinks); } @@ -183,6 +193,7 @@ public final class Channel extends IChannel.Stub implements Comparable<Channel> return userCount; } + @Override public int getPermissions() { return mPermissions; } diff --git a/src/main/java/com/morlunk/jumble/model/IChannel.java b/src/main/java/com/morlunk/jumble/model/IChannel.java new file mode 100644 index 0000000..fc9d8e7 --- /dev/null +++ b/src/main/java/com/morlunk/jumble/model/IChannel.java @@ -0,0 +1,46 @@ +/* + * 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.model; + +import java.util.List; + +public interface IChannel { + List<? extends IUser> getUsers(); + + int getId(); + + int getPosition(); + + boolean isTemporary(); + + IChannel getParent(); + + String getName(); + + String getDescription(); + + byte[] getDescriptionHash(); + + List<? extends IChannel> getSubchannels(); + + int getSubchannelUserCount(); + + List<? extends IChannel> getLinks(); + + int getPermissions(); +} diff --git a/src/main/java/com/morlunk/jumble/model/IMessage.java b/src/main/java/com/morlunk/jumble/model/IMessage.java new file mode 100644 index 0000000..9a3ec2c --- /dev/null +++ b/src/main/java/com/morlunk/jumble/model/IMessage.java @@ -0,0 +1,36 @@ +/* + * 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.model; + +import java.util.List; + +public interface IMessage { + int getActor(); + + String getActorName(); + + List<Channel> getTargetChannels(); + + List<Channel> getTargetTrees(); + + List<User> getTargetUsers(); + + String getMessage(); + + long getReceivedTime(); +} diff --git a/src/main/java/com/morlunk/jumble/model/IUser.java b/src/main/java/com/morlunk/jumble/model/IUser.java new file mode 100644 index 0000000..5f18246 --- /dev/null +++ b/src/main/java/com/morlunk/jumble/model/IUser.java @@ -0,0 +1,65 @@ +/* + * 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.model; + +/** + * Created by andrew on 15/10/15. + */ +public interface IUser { + int getSession(); + + Channel getChannel(); + + int getUserId(); + + String getName(); + + String getComment(); + + byte[] getCommentHash(); + + byte[] getTexture(); + + byte[] getTextureHash(); + + String getHash(); + + boolean isMuted(); + + boolean isDeafened(); + + boolean isSuppressed(); + + boolean isSelfMuted(); + + boolean isSelfDeafened(); + + boolean isPrioritySpeaker(); + + boolean isRecording(); + + boolean isLocalMuted(); + + boolean isLocalIgnored(); + + void setLocalMuted(boolean muted); + + void setLocalIgnored(boolean ignored); + + TalkState getTalkState(); +} diff --git a/src/main/java/com/morlunk/jumble/model/Message.java b/src/main/java/com/morlunk/jumble/model/Message.java index 17270da..202e803 100644 --- a/src/main/java/com/morlunk/jumble/model/Message.java +++ b/src/main/java/com/morlunk/jumble/model/Message.java @@ -32,7 +32,7 @@ import java.util.List; * as the actor may no longer be on the server. * Created by andrew on 03/12/13. */ -public class Message extends IMessage.Stub { +public class Message implements IMessage { private int mActor; private String mActorName; private List<Channel> mChannels; @@ -58,30 +58,37 @@ public class Message extends IMessage.Stub { mTrees = trees; mUsers = users; } + @Override public int getActor() { return mActor; } + @Override public String getActorName() { return mActorName; } + @Override public List<Channel> getTargetChannels() { return Collections.unmodifiableList(mChannels); } + @Override public List<Channel> getTargetTrees() { return Collections.unmodifiableList(mTrees); } + @Override public List<User> getTargetUsers() { return Collections.unmodifiableList(mUsers); } + @Override public String getMessage() { return mMessage; } + @Override public long getReceivedTime() { return mReceivedTime; } diff --git a/src/main/java/com/morlunk/jumble/model/User.java b/src/main/java/com/morlunk/jumble/model/User.java index 559cbdf..3bfd31d 100644 --- a/src/main/java/com/morlunk/jumble/model/User.java +++ b/src/main/java/com/morlunk/jumble/model/User.java @@ -19,7 +19,7 @@ package com.morlunk.jumble.model; import com.google.protobuf.ByteString; -public class User extends IUser.Stub implements Comparable<User> { +public class User implements IUser, Comparable<User> { private int mSession; private int mId = -1; @@ -60,10 +60,12 @@ public class User extends IUser.Stub implements Comparable<User> { mName = name; } + @Override public int getSession() { return mSession; } + @Override public Channel getChannel() { return mChannel; } @@ -82,6 +84,7 @@ public class User extends IUser.Stub implements Comparable<User> { mChannel.addUser(this); } + @Override public int getUserId() { return mId; } @@ -90,6 +93,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mId = mId; } + @Override public String getName() { return mName; } @@ -98,6 +102,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mName = mName; } + @Override public String getComment() { return mComment; } @@ -106,6 +111,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mComment = mComment; } + @Override public byte[] getCommentHash() { return mCommentHash != null ? mCommentHash.toByteArray() : null; } @@ -114,6 +120,7 @@ public class User extends IUser.Stub implements Comparable<User> { mCommentHash = commentHash; } + @Override public byte[] getTexture() { return mTexture != null ? mTexture.toByteArray() : null; } @@ -122,6 +129,7 @@ public class User extends IUser.Stub implements Comparable<User> { mTexture = texture; } + @Override public byte[] getTextureHash() { return mTextureHash != null ? mTextureHash.toByteArray() : null; } @@ -130,6 +138,7 @@ public class User extends IUser.Stub implements Comparable<User> { mTextureHash = textureHash; } + @Override public String getHash() { return mHash; } @@ -138,6 +147,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mHash = mHash; } + @Override public boolean isMuted() { return mMuted; } @@ -146,6 +156,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mMuted = mMuted; } + @Override public boolean isDeafened() { return mDeafened; } @@ -154,6 +165,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mDeafened = mDeafened; } + @Override public boolean isSuppressed() { return mSuppressed; } @@ -162,6 +174,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mSuppressed = mSuppressed; } + @Override public boolean isSelfMuted() { return mSelfMuted; } @@ -170,6 +183,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mSelfMuted = mSelfMuted; } + @Override public boolean isSelfDeafened() { return mSelfDeafened; } @@ -178,6 +192,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mSelfDeafened = mSelfDeafened; } + @Override public boolean isPrioritySpeaker() { return mPrioritySpeaker; } @@ -186,6 +201,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mPrioritySpeaker = mPrioritySpeaker; } + @Override public boolean isRecording() { return mRecording; } @@ -194,6 +210,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mRecording = mRecording; } + @Override public boolean isLocalMuted() { return mLocalMuted; } @@ -202,6 +219,7 @@ public class User extends IUser.Stub implements Comparable<User> { this.mLocalMuted = mLocalMuted; } + @Override public boolean isLocalIgnored() { return mLocalIgnored; } @@ -210,6 +228,7 @@ public class User extends IUser.Stub implements Comparable<User> { mLocalIgnored = localIgnored; } + @Override public TalkState getTalkState() { return mTalkState; } diff --git a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java index 2bbbad9..6f54199 100644 --- a/src/main/java/com/morlunk/jumble/net/JumbleConnection.java +++ b/src/main/java/com/morlunk/jumble/net/JumbleConnection.java @@ -25,6 +25,8 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import com.morlunk.jumble.Constants; +import com.morlunk.jumble.exception.NotConnectedException; +import com.morlunk.jumble.exception.NotSynchronizedException; import com.morlunk.jumble.protobuf.Mumble; import com.morlunk.jumble.protocol.JumbleTCPMessageListener; import com.morlunk.jumble.protocol.JumbleUDPMessageListener; @@ -398,31 +400,45 @@ public class JumbleConnection implements JumbleTCP.TCPConnectionListener, Jumble mTrustStoreFormat = format; } - public int getServerVersion() { + public int getServerVersion() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); return mServerVersion; } - public String getServerRelease() { + public String getServerRelease() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); return mServerRelease; } - public String getServerOSName() { + public String getServerOSName() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); return mServerOSName; } - public String getServerOSVersion() { + public String getServerOSVersion() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); return mServerOSVersion; } - public long getTCPLatency() { + public long getTCPLatency() throws NotConnectedException { + if (!isConnected()) + throw new NotConnectedException(); return mLastTCPPing; } - public long getUDPLatency() { + public long getUDPLatency() throws NotConnectedException { + if (!isConnected()) + throw new NotConnectedException(); return mLastUDPPing; } - public int getSession() { + public int getSession() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException("Session is set during synchronization"); return mSession; } @@ -430,11 +446,15 @@ public class JumbleConnection implements JumbleTCP.TCPConnectionListener, Jumble * Returns the server-reported maximum input bandwidth, or -1 if not set. * @return the input bandwidth in bps, or -1 if not set. */ - public int getMaxBandwidth() { + public int getMaxBandwidth() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); return mMaxBandwidth; } - public JumbleUDPMessageType getCodec() { + public JumbleUDPMessageType getCodec() throws NotSynchronizedException { + if (!isSynchronized()) + throw new NotSynchronizedException(); return mCodec; } diff --git a/src/main/java/com/morlunk/jumble/protocol/ModelHandler.java b/src/main/java/com/morlunk/jumble/protocol/ModelHandler.java index 973fb3a..a14b803 100644 --- a/src/main/java/com/morlunk/jumble/protocol/ModelHandler.java +++ b/src/main/java/com/morlunk/jumble/protocol/ModelHandler.java @@ -24,13 +24,13 @@ import android.os.RemoteException; import android.util.Log; import com.morlunk.jumble.Constants; -import com.morlunk.jumble.IJumbleObserver; import com.morlunk.jumble.R; import com.morlunk.jumble.model.Channel; import com.morlunk.jumble.model.Message; import com.morlunk.jumble.model.User; import com.morlunk.jumble.protobuf.Mumble; import com.morlunk.jumble.protocol.JumbleTCPMessageListener; +import com.morlunk.jumble.util.IJumbleObserver; import com.morlunk.jumble.util.JumbleLogger; import com.morlunk.jumble.util.MessageFormatter; @@ -165,15 +165,10 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { channel.addLink(mChannels.get(link)); } - final Channel finalChannel = channel; - try { - if(newChannel) - mObserver.onChannelAdded(finalChannel); - else - mObserver.onChannelStateUpdated(finalChannel); - } catch (RemoteException e) { - e.printStackTrace(); - } + if(newChannel) + mObserver.onChannelAdded(channel); + else + mObserver.onChannelStateUpdated(channel); } @Override @@ -185,11 +180,7 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { if(parent != null) { parent.removeSubchannel(channel); } - try { - mObserver.onChannelRemoved(channel); - } catch (RemoteException e) { - e.printStackTrace(); - } + mObserver.onChannelRemoved(channel); } } @@ -204,11 +195,7 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { channel.setPermissions(msg.getPermissions()); if(msg.getChannelId() == 0) // If we're provided permissions for the root channel, we'll apply these as our server permissions. mPermissions = channel.getPermissions(); - try { - mObserver.onChannelPermissionsUpdated(channel); - } catch (RemoteException e) { - e.printStackTrace(); - } + mObserver.onChannelPermissionsUpdated(channel); } } @@ -343,11 +330,7 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { user.setChannel(channel); if(!newUser) { - try { - mObserver.onUserJoinedChannel(finalUser, channel, old); - } catch (RemoteException e) { - e.printStackTrace(); - } + mObserver.onUserJoinedChannel(finalUser, channel, old); } Channel sessionChannel = self != null ? self.getChannel() : null; @@ -402,16 +385,10 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { if(msg.hasComment()) user.setComment(msg.getComment()); - final boolean finalNewUser = newUser; - - try { - if(finalNewUser) - mObserver.onUserConnected(finalUser); - else - mObserver.onUserStateUpdated(finalUser); - } catch (RemoteException e) { - e.printStackTrace(); - } + if (newUser) + mObserver.onUserConnected(user); + else + mObserver.onUserStateUpdated(user); } @Override @@ -428,12 +405,7 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { mLogger.logInfo(mContext.getString(R.string.chat_notify_disconnected, MessageFormatter.highlightString(user.getName()))); user.setChannel(null); - - try { - mObserver.onUserRemoved(user, reason); - } catch (RemoteException e) { - e.printStackTrace(); - } + mObserver.onUserRemoved(user, reason); } @Override @@ -466,11 +438,7 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { else reason = mContext.getString(R.string.perm_denied); } - try { - mObserver.onPermissionDenied(reason); - } catch (RemoteException e) { - e.printStackTrace(); - } + mObserver.onPermissionDenied(reason); } @Override @@ -490,11 +458,7 @@ public class ModelHandler extends JumbleTCPMessageListener.Stub { String actorName = sender != null ? sender.getName() : mContext.getString(R.string.server); Message message = new Message(msg.getActor(), actorName, channels, trees, users, msg.getMessage()); - try { - mObserver.onMessageLogged(message); - } catch (RemoteException e) { - e.printStackTrace(); - } + mObserver.onMessageLogged(message); } @Override diff --git a/src/main/java/com/morlunk/jumble/util/IJumbleObserver.java b/src/main/java/com/morlunk/jumble/util/IJumbleObserver.java new file mode 100644 index 0000000..d167da3 --- /dev/null +++ b/src/main/java/com/morlunk/jumble/util/IJumbleObserver.java @@ -0,0 +1,65 @@ +/* + * 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.util; + +import com.morlunk.jumble.model.IChannel; +import com.morlunk.jumble.model.IMessage; +import com.morlunk.jumble.model.IUser; + +import java.security.cert.X509Certificate; + +/** + * Created by andrew on 18/10/15. + */ +public interface IJumbleObserver { + void onConnected(); + + void onConnecting(); + + void onDisconnected(JumbleException e); + + void onTLSHandshakeFailed(X509Certificate[] chain); + + void onChannelAdded(IChannel channel); + + void onChannelStateUpdated(IChannel channel); + + void onChannelRemoved(IChannel channel); + + void onChannelPermissionsUpdated(IChannel channel); + + void onUserConnected(IUser user); + + void onUserStateUpdated(IUser user); + + void onUserTalkStateUpdated(IUser user); + + void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel); + + void onUserRemoved(IUser user, String reason); + + void onPermissionDenied(String reason); + + void onMessageLogged(IMessage message); + + void onLogInfo(String message); + + void onLogWarning(String message); + + void onLogError(String message); +} diff --git a/src/main/java/com/morlunk/jumble/util/JumbleCallbacks.java b/src/main/java/com/morlunk/jumble/util/JumbleCallbacks.java index 1f67a16..5fc1107 100644 --- a/src/main/java/com/morlunk/jumble/util/JumbleCallbacks.java +++ b/src/main/java/com/morlunk/jumble/util/JumbleCallbacks.java @@ -17,214 +17,158 @@ package com.morlunk.jumble.util; -import android.os.RemoteCallbackList; -import android.os.RemoteException; - -import com.morlunk.jumble.IJumbleObserver; import com.morlunk.jumble.model.IChannel; import com.morlunk.jumble.model.IMessage; import com.morlunk.jumble.model.IUser; +import org.spongycastle.jcajce.provider.asymmetric.X509; + +import java.security.cert.X509Certificate; +import java.util.HashSet; +import java.util.Set; + /** * A composite wrapper around Jumble observers to easily broadcast to each observer. * Created by andrew on 12/07/14. */ -public class JumbleCallbacks extends JumbleObserver.Stub { - private final RemoteCallbackList<IJumbleObserver> mCallbacks; +public class JumbleCallbacks implements IJumbleObserver { + private final Set<IJumbleObserver> mCallbacks; public JumbleCallbacks() { - mCallbacks = new RemoteCallbackList<IJumbleObserver>(); + mCallbacks = new HashSet<>(); } public void registerObserver(IJumbleObserver observer) { - mCallbacks.register(observer); + mCallbacks.add(observer); } public void unregisterObserver(IJumbleObserver observer) { - mCallbacks.unregister(observer); - } - - public void kill() { - mCallbacks.kill(); + mCallbacks.remove(observer); } @Override - public void onConnected() throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onConnected(); + public void onConnected() { + for (IJumbleObserver observer : mCallbacks) { + observer.onConnected(); } - mCallbacks.finishBroadcast(); } @Override - public void onConnecting() throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onConnecting(); + public void onConnecting() { + for (IJumbleObserver observer : mCallbacks) { + observer.onConnecting(); } - mCallbacks.finishBroadcast(); } @Override - public void onDisconnected(JumbleException e) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onDisconnected(e); + public void onDisconnected(JumbleException e) { + for (IJumbleObserver observer : mCallbacks) { + observer.onDisconnected(e); } - mCallbacks.finishBroadcast(); } @Override - public void onTLSHandshakeFailed(ParcelableByteArray cert) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onTLSHandshakeFailed(cert); + public void onTLSHandshakeFailed(X509Certificate[] chain) { + for (IJumbleObserver observer : mCallbacks) { + observer.onTLSHandshakeFailed(chain); } - mCallbacks.finishBroadcast(); } @Override - public void onChannelAdded(IChannel channel) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onChannelAdded(channel); + public void onChannelAdded(IChannel channel) { + for (IJumbleObserver observer : mCallbacks) { + observer.onChannelAdded(channel); } - mCallbacks.finishBroadcast(); } @Override - public void onChannelStateUpdated(IChannel channel) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onChannelStateUpdated(channel); + public void onChannelStateUpdated(IChannel channel) { + for (IJumbleObserver observer : mCallbacks) { + observer.onChannelStateUpdated(channel); } - mCallbacks.finishBroadcast(); } @Override - public void onChannelRemoved(IChannel channel) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onChannelRemoved(channel); + public void onChannelRemoved(IChannel channel) { + for (IJumbleObserver observer : mCallbacks) { + observer.onChannelRemoved(channel); } - mCallbacks.finishBroadcast(); } @Override - public void onChannelPermissionsUpdated(IChannel channel) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onChannelPermissionsUpdated(channel); + public void onChannelPermissionsUpdated(IChannel channel) { + for (IJumbleObserver observer : mCallbacks) { + observer.onChannelPermissionsUpdated(channel); } - mCallbacks.finishBroadcast(); } @Override - public void onUserConnected(IUser user) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onUserConnected(user); + public void onUserConnected(IUser user) { + for (IJumbleObserver observer : mCallbacks) { + observer.onUserConnected(user); } - mCallbacks.finishBroadcast(); } @Override - public void onUserStateUpdated(IUser user) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onUserStateUpdated(user); + public void onUserStateUpdated(IUser user) { + for (IJumbleObserver observer : mCallbacks) { + observer.onUserStateUpdated(user); } - mCallbacks.finishBroadcast(); } @Override - public void onUserTalkStateUpdated(IUser user) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onUserTalkStateUpdated(user); + public void onUserTalkStateUpdated(IUser user) { + for (IJumbleObserver observer : mCallbacks) { + observer.onUserTalkStateUpdated(user); } - mCallbacks.finishBroadcast(); } @Override - public void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onUserJoinedChannel(user, newChannel, oldChannel); + public void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel) { + for (IJumbleObserver observer : mCallbacks) { + observer.onUserJoinedChannel(user, newChannel, oldChannel); } - mCallbacks.finishBroadcast(); } @Override - public void onUserRemoved(IUser user, String reason) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onUserRemoved(user, reason); + public void onUserRemoved(IUser user, String reason) { + for (IJumbleObserver observer : mCallbacks) { + observer.onUserRemoved(user, reason); } - mCallbacks.finishBroadcast(); } @Override - public void onPermissionDenied(String reason) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onPermissionDenied(reason); + public void onPermissionDenied(String reason) { + for (IJumbleObserver observer : mCallbacks) { + observer.onPermissionDenied(reason); } - mCallbacks.finishBroadcast(); } @Override - public void onMessageLogged(IMessage message) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onMessageLogged(message); + public void onMessageLogged(IMessage message) { + for (IJumbleObserver observer : mCallbacks) { + observer.onMessageLogged(message); } - mCallbacks.finishBroadcast(); } @Override - public void onLogInfo(String message) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onLogInfo(message); + public void onLogInfo(String message) { + for (IJumbleObserver observer : mCallbacks) { + observer.onLogInfo(message); } - mCallbacks.finishBroadcast(); } @Override - public void onLogWarning(String message) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onLogWarning(message); + public void onLogWarning(String message) { + for (IJumbleObserver observer : mCallbacks) { + observer.onLogWarning(message); } - mCallbacks.finishBroadcast(); } @Override - public void onLogError(String message) throws RemoteException { - int i = mCallbacks.beginBroadcast(); - while(i > 0) { - i--; - mCallbacks.getBroadcastItem(i).onLogError(message); + public void onLogError(String message) { + for (IJumbleObserver observer : mCallbacks) { + observer.onLogError(message); } - mCallbacks.finishBroadcast(); } } diff --git a/src/main/java/com/morlunk/jumble/util/JumbleObserver.java b/src/main/java/com/morlunk/jumble/util/JumbleObserver.java index 3c51383..91f2751 100644 --- a/src/main/java/com/morlunk/jumble/util/JumbleObserver.java +++ b/src/main/java/com/morlunk/jumble/util/JumbleObserver.java @@ -17,109 +17,104 @@ package com.morlunk.jumble.util; -import android.os.RemoteException; - -import com.morlunk.jumble.IJumbleObserver; -import com.morlunk.jumble.model.Channel; import com.morlunk.jumble.model.IChannel; import com.morlunk.jumble.model.IMessage; import com.morlunk.jumble.model.IUser; -import com.morlunk.jumble.model.Message; -import com.morlunk.jumble.model.User; + +import java.security.cert.X509Certificate; /** * Stub class for Jumble service observation. * Created by andrew on 31/07/13. */ -public class JumbleObserver extends IJumbleObserver.Stub { +public class JumbleObserver implements IJumbleObserver { @Override - public void onConnected() throws RemoteException { + public void onConnected() { } @Override - public void onConnecting() throws RemoteException { + public void onConnecting() { } @Override - public void onDisconnected(JumbleException e) throws RemoteException { + public void onDisconnected(JumbleException e) { } @Override - public void onTLSHandshakeFailed(ParcelableByteArray cert) throws RemoteException { + public void onTLSHandshakeFailed(X509Certificate[] chain) { } @Override - public void onChannelAdded(IChannel channel) throws RemoteException { + public void onChannelAdded(IChannel channel) { } @Override - public void onChannelStateUpdated(IChannel channel) throws RemoteException { + public void onChannelStateUpdated(IChannel channel) { } @Override - public void onChannelRemoved(IChannel channel) throws RemoteException { + public void onChannelRemoved(IChannel channel) { } @Override - public void onChannelPermissionsUpdated(IChannel channel) throws RemoteException { + public void onChannelPermissionsUpdated(IChannel channel) { } @Override - public void onUserConnected(IUser user) throws RemoteException { + public void onUserConnected(IUser user) { } @Override - public void onUserStateUpdated(IUser user) throws RemoteException { + public void onUserStateUpdated(IUser user) { } @Override - public void onUserTalkStateUpdated(IUser user) throws RemoteException { - + public void onUserTalkStateUpdated(IUser user) { + } @Override - public void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel) throws RemoteException { + public void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel) { } @Override - public void onUserRemoved(IUser user, String reason) throws RemoteException { + public void onUserRemoved(IUser user, String reason) { } @Override - public void onPermissionDenied(String reason) throws RemoteException { + public void onPermissionDenied(String reason) { } @Override - public void onMessageLogged(IMessage message) throws RemoteException { + public void onMessageLogged(IMessage message) { } @Override - public void onLogInfo(String message) throws RemoteException { + public void onLogInfo(String message) { } @Override - public void onLogWarning(String message) throws RemoteException { + public void onLogWarning(String message) { } @Override - public void onLogError(String message) throws RemoteException { + public void onLogError(String message) { } - } diff --git a/src/main/java/com/morlunk/jumble/util/ParcelableByteArray.java b/src/main/java/com/morlunk/jumble/util/ParcelableByteArray.java deleted file mode 100644 index 801cb0a..0000000 --- a/src/main/java/com/morlunk/jumble/util/ParcelableByteArray.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2014 Andrew Comminos - * - * 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.util; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Created by andrew on 05/04/14. - */ -public class ParcelableByteArray implements Parcelable { - - private byte[] mByteArray; - - public ParcelableByteArray(byte[] array) { - mByteArray = array; - } - - private ParcelableByteArray(Parcel in) { - int length = in.readInt(); - mByteArray = new byte[length]; - in.readByteArray(mByteArray); - } - - public byte[] getBytes() { - return mByteArray; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mByteArray.length); - dest.writeByteArray(mByteArray); - } - - public static final Creator<ParcelableByteArray> CREATOR = new Creator<ParcelableByteArray>() { - - @Override - public ParcelableByteArray createFromParcel(Parcel source) { - return new ParcelableByteArray(source); - } - - @Override - public ParcelableByteArray[] newArray(int size) { - return new ParcelableByteArray[size]; - } - }; -} |