From 6b2ef30888dbcde462670f2bd6aeeb60b006b6ce Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 7 Nov 2014 18:25:53 +0200 Subject: [PATCH] Remove device from ContactsFragment when disconnected. Also changed some variable/class/function names. --- .../ensichat/bluetooth/ChatService.scala | 46 +++++++++++-------- .../ensichat/bluetooth/ConnectThread.scala | 2 +- .../ensichat/bluetooth/TransferThread.scala | 37 +++++++-------- .../ensichat/fragments/ContactsFragment.scala | 6 +-- 4 files changed, 49 insertions(+), 42 deletions(-) diff --git a/app/src/main/scala/com/nutomic/ensichat/bluetooth/ChatService.scala b/app/src/main/scala/com/nutomic/ensichat/bluetooth/ChatService.scala index 8053db5..198c9a3 100644 --- a/app/src/main/scala/com/nutomic/ensichat/bluetooth/ChatService.scala +++ b/app/src/main/scala/com/nutomic/ensichat/bluetooth/ChatService.scala @@ -8,7 +8,7 @@ import android.content.{BroadcastReceiver, Context, Intent, IntentFilter} import android.os.Handler import android.util.Log import com.nutomic.ensichat.R -import com.nutomic.ensichat.bluetooth.ChatService.{OnDeviceConnectedListener, OnMessageReceivedListener} +import com.nutomic.ensichat.bluetooth.ChatService.{OnConnectionChangedListener, OnMessageReceivedListener} import com.nutomic.ensichat.messages._ import scala.collection.immutable.{HashMap, HashSet, TreeSet} @@ -24,8 +24,8 @@ object ChatService { val KEY_GENERATION_FINISHED = "com.nutomic.ensichat.messages.KEY_GENERATION_FINISHED" - trait OnDeviceConnectedListener { - def onDeviceConnected(devices: Map[Device.ID, Device]): Unit + trait OnConnectionChangedListener { + def onConnectionChanged(devices: Map[Device.ID, Device]): Unit } trait OnMessageReceivedListener { @@ -52,7 +52,7 @@ class ChatService extends Service { * but on a Nexus S (Android 4.1.2), these functions are garbage collected even when * referenced. */ - private var deviceListeners = new HashSet[WeakReference[OnDeviceConnectedListener]]() + private var connectionListeners = new HashSet[WeakReference[OnConnectionChangedListener]]() private val messageListeners = mutable.HashMap[Device.ID, mutable.Set[WeakReference[OnMessageReceivedListener]]]() @@ -137,7 +137,7 @@ class ChatService extends Service { val device: Device = new Device(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), false) devices += (device.id -> device) - new ConnectThread(device, onConnected).start() + new ConnectThread(device, onConnectionChanged).start() } } @@ -168,34 +168,40 @@ class ChatService extends Service { private def startBluetoothConnections(): Unit = { cancelDiscovery = false discover() - ListenThread = new ListenThread(getString(R.string.app_name), bluetoothAdapter, onConnected) + ListenThread = + new ListenThread(getString(R.string.app_name), bluetoothAdapter, onConnectionChanged) ListenThread.start() } /** * Registers a listener that is called whenever a new device is connected. */ - def registerDeviceListener(listener: OnDeviceConnectedListener): Unit = { - deviceListeners += new WeakReference[OnDeviceConnectedListener](listener) - listener.onDeviceConnected(devices) + def registerConnectionListener(listener: OnConnectionChangedListener): Unit = { + connectionListeners += new WeakReference[OnConnectionChangedListener](listener) + listener.onConnectionChanged(devices) } /** * Called when a Bluetooth device is connected. * - * Adds the device to [[connections]], notifies all [[deviceListeners]], sends DeviceInfoMessage. + * Adds the device to [[connections]], notifies all [[connectionListeners]], sends DeviceInfoMessage. + * + * @param device The updated device info for the remote device. + * @param socket A socket for data transfer if device.connected is true, otherwise null. */ - def onConnected(device: Device, socket: BluetoothSocket): Unit = { - val updatedDevice: Device = new Device(device.bluetoothDevice, true) - devices += (device.id -> updatedDevice) - connections += (device.id -> - new TransferThread(updatedDevice, socket, localDeviceId, Encrypt, handleNewMessage)) - connections(device.id).start() + def onConnectionChanged(device: Device, socket: BluetoothSocket): Unit = { + devices += (device.id -> device) - send(new DeviceInfoMessage(localDeviceId, device.id, new Date(), Encrypt.getLocalPublicKey)) - deviceListeners.foreach(l => l.get match { - case Some(_) => l.apply().onDeviceConnected(devices) - case None => deviceListeners -= l + if (device.connected) { + connections += (device.id -> + new TransferThread(device, socket, this, Encrypt, handleNewMessage)) + connections(device.id).start() + send(new DeviceInfoMessage(localDeviceId, device.id, new Date(), Encrypt.getLocalPublicKey)) + } + + connectionListeners.foreach(l => l.get match { + case Some(_) => l.apply().onConnectionChanged(devices) + case None => connectionListeners -= l }) } diff --git a/app/src/main/scala/com/nutomic/ensichat/bluetooth/ConnectThread.scala b/app/src/main/scala/com/nutomic/ensichat/bluetooth/ConnectThread.scala index 48f8cff..0c58250 100644 --- a/app/src/main/scala/com/nutomic/ensichat/bluetooth/ConnectThread.scala +++ b/app/src/main/scala/com/nutomic/ensichat/bluetooth/ConnectThread.scala @@ -33,7 +33,7 @@ class ConnectThread(device: Device, onConnected: (Device, BluetoothSocket) => Un } Log.i(Tag, "Successfully connected to device " + device.name) - onConnected(device, Socket) + onConnected(new Device(device.bluetoothDevice, true), Socket) } } diff --git a/app/src/main/scala/com/nutomic/ensichat/bluetooth/TransferThread.scala b/app/src/main/scala/com/nutomic/ensichat/bluetooth/TransferThread.scala index f98cf35..fdfaa82 100644 --- a/app/src/main/scala/com/nutomic/ensichat/bluetooth/TransferThread.scala +++ b/app/src/main/scala/com/nutomic/ensichat/bluetooth/TransferThread.scala @@ -13,11 +13,11 @@ import com.nutomic.ensichat.messages.{Crypto, DeviceInfoMessage, Message, TextMe * * @param device The bluetooth device to interact with. * @param socket An open socket to the given device. - * @param encrypt Object used to handle signing and encryption of messages. + * @param crypto Object used to handle signing and encryption of messages. * @param onReceive Called when a message was received from the other device. */ -class TransferThread(device: Device, socket: BluetoothSocket, localDevice: Device.ID, - encrypt: Crypto, onReceive: (Message) => Unit) extends Thread { +class TransferThread(device: Device, socket: BluetoothSocket, service: ChatService, + crypto: Crypto, onReceive: (Message) => Unit) extends Thread { private val Tag: String = "TransferThread" @@ -44,58 +44,59 @@ class TransferThread(device: Device, socket: BluetoothSocket, localDevice: Devic override def run(): Unit = { Log.i(Tag, "Starting data transfer with " + device.toString) - // Keep listening to the InputStream while connected - while (true) { + while (socket.isConnected) { try { val bytes = new Array[Byte](MaxMessageLength) InStream.read(bytes) - val (msg, signature) = Message.read(bytes) + val (message, signature) = Message.read(bytes) var messageValid = true - if (msg.sender != device.id) { + if (message.sender != device.id) { Log.i(Tag, "Dropping message with invalid sender from " + device.id) messageValid = false } - if (msg.receiver != localDevice) { + if (message.receiver != service.localDeviceId) { Log.i(Tag, "Dropping message with different receiver from " + device.id) messageValid = false } // Add public key for new, directly connected device. // Explicitly check that message was not forwarded or spoofed. - if (msg.isInstanceOf[DeviceInfoMessage] && !encrypt.havePublicKey(msg.sender) && - msg.sender == device.id) { - val dim = msg.asInstanceOf[DeviceInfoMessage] + if (message.isInstanceOf[DeviceInfoMessage] && !crypto.havePublicKey(message.sender) && + message.sender == device.id) { + val dim = message.asInstanceOf[DeviceInfoMessage] // Permanently store public key for new local devices (also check signature). - if (encrypt.isValidSignature(msg, signature, dim.publicKey)) { - encrypt.addPublicKey(device.id, dim.publicKey) + if (crypto.isValidSignature(message, signature, dim.publicKey)) { + crypto.addPublicKey(device.id, dim.publicKey) Log.i(Tag, "Added public key for new device " + device.name) } } - if (!encrypt.isValidSignature(msg, signature)) { + if (!crypto.isValidSignature(message, signature)) { Log.i(Tag, "Dropping message with invalid signature from " + device.id) messageValid = false } if (messageValid) { - msg match { + message match { case m: TextMessage => onReceive(m) - case m: DeviceInfoMessage => encrypt.addPublicKey(msg.sender, m.publicKey) + case m: DeviceInfoMessage => crypto.addPublicKey(message.sender, m.publicKey) } } } catch { case e: IOException => - Log.e(Tag, "Disconnected from device", e) + Log.w(Tag, "Connection to " + device.name + " closed with exception", e) + service.onConnectionChanged(new Device(device.bluetoothDevice, false), null) return } } + service.onConnectionChanged(new Device(device.bluetoothDevice, false), null) } def send(message: Message): Unit = { try { - val sig = encrypt.calculateSignature(message) + val sig = crypto.calculateSignature(message) val bytes = message.write(sig) OutStream.write(bytes) } catch { diff --git a/app/src/main/scala/com/nutomic/ensichat/fragments/ContactsFragment.scala b/app/src/main/scala/com/nutomic/ensichat/fragments/ContactsFragment.scala index cb44a95..c46bf1a 100644 --- a/app/src/main/scala/com/nutomic/ensichat/fragments/ContactsFragment.scala +++ b/app/src/main/scala/com/nutomic/ensichat/fragments/ContactsFragment.scala @@ -13,7 +13,7 @@ import com.nutomic.ensichat.util.DevicesAdapter /** * Lists all nearby, connected devices. */ -class ContactsFragment extends ListFragment with ChatService.OnDeviceConnectedListener { +class ContactsFragment extends ListFragment with ChatService.OnConnectionChangedListener { private var chatService: ChatService = _ @@ -21,7 +21,7 @@ class ContactsFragment extends ListFragment with ChatService.OnDeviceConnectedLi override def onServiceConnected(componentName: ComponentName, iBinder: IBinder): Unit = { val binder: ChatServiceBinder = iBinder.asInstanceOf[ChatServiceBinder] chatService = binder.getService - chatService.registerDeviceListener(ContactsFragment.this) + chatService.registerConnectionListener(ContactsFragment.this) } override def onServiceDisconnected(componentName: ComponentName): Unit = { @@ -71,7 +71,7 @@ class ContactsFragment extends ListFragment with ChatService.OnDeviceConnectedLi /** * Displays newly connected devices in the list. */ - override def onDeviceConnected(devices: Map[Device.ID, Device]): Unit = { + override def onConnectionChanged(devices: Map[Device.ID, Device]): Unit = { if (getActivity == null) return