I am working on the project on the android dev. the website for BluetoothChat and it seems to be sending messages correctly but not receiving them. In the past,
I attempted to create another Bluetooth app and when connecting to a device I was prompted to confirm the connection and presented with a pin code. when working on the current app I am never given a prompt. However, it is not hitting any exceptions and messages seem to be sending. Here is my Thread class for managing a connection:
inner class ConnectedThread(val socket:BluetoothSocket, val socketType:String) : Thread(){
val inStream = socket.inputStream
val outStream = socket.outputStream
init {
mState = STATE_CONNECTED
}
override fun run() {
Log.i(TAG, "BEGIN mConnectedThread")
val buffer = ByteArray(1024)
var bytes:Int
while (mState == STATE_CONNECTED){
try {
bytes = inStream.read(buffer)
mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget()
}catch (ioe:IOException){
Log.e(TAG, "disconnected", ioe)
connectionLost()
break
}
}
}
fun write(buffer:ByteArray){
try {
outStream.writeMessage(buffer, mHandler)
}catch (ioe:IOException){
Log.e(TAG, "Exception during write", ioe)
}
}
fun cancel(){
try {
socket.close()
}catch (ioe:IOException){
Log.e(TAG, "close of connect socket failed", ioe)
}
}
}
}
and below i it the extension function used to write to the OutputStream:
fun OutputStream.writeMessage(buffer:ByteArray, handler:Handler? = null){
write(buffer)
handler?.let {
it.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget()
}
}
I just am not sure where my mistake is. I was thinking it was a problem with the way that I am sending the message? But when I step through my code with the debugger I really don't see anything out of place. Now I am thinking that the problem is at the end of the receiving device which doesn't seem to receive anything at all. I will post my Handler and broadcastReceiver as well:
val mHandler = object:Handler(){
override fun handleMessage(msg: Message?) {
when(msg!!.what) {
Constants.MESSAGE_STATE_CHANGE -> {
when(msg.arg1) {
BluetoothChatService.STATE_CONNECTED -> {
setStatus(getString(R.string.title_connected_to, mConnectedDeviceName))
mConversationArrayAdapter!!.clear()
}
BluetoothChatService.STATE_CONNECTING -> {
setStatus(getString(R.string.title_connecting))
}
BluetoothChatService.STATE_LISTEN -> {
}
BluetoothChatService.STATE_NONE -> {
setStatus(getString(R.string.title_not_connected))
}
}
}
Constants.MESSAGE_WRITE -> {
val writeBuf = msg.obj as ByteArray
val writeMessage = String(writeBuf)
mConversationArrayAdapter!!.add("Me: $writeMessage")
}
Constants.MESSAGE_READ -> {
val readBuf = msg.obj as ByteArray
val readMessage = String(readBuf, 0, msg.arg1)
mConversationArrayAdapter!!.add("$mConnectedDeviceName: $readMessage")
}
Constants.MESSAGE_DEVICE_NAME -> {
mConnectedDeviceName = msg.data.getString(Constants.DEVICE_NAME)
if (activity!=null)Toast.makeText(activity, "Connected to $mConnectedDeviceName", Toast.LENGTH_SHORT).show()
}
Constants.MESSAGE_TOAST -> {
if (activity!=null)Toast.makeText(activity, msg.data.getString(Constants.TOAST), Toast.LENGTH_SHORT).show()
}
}
}
}
val mReceiver:BroadcastReceiver = object:BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (BluetoothDevice.ACTION_FOUND.equals(action)){
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
if (device.bondState!=BluetoothDevice.BOND_BONDED){
mNewDevicesArrayAdapter.add("${device.name} \n ${device.address}")
}
}else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
setProgressBarIndeterminateVisibility(false)
setTitle(R.string.select_device)
if (mNewDevicesArrayAdapter.count==0){
val noDevices = resources.getText(R.string.none_found).toString()
mNewDevicesArrayAdapter.add(noDevices)
}
}
}
}
Here is my AcceptThread as well:
inner class AcceptThread(val secure:Boolean) : Thread(){
var mmServerSocket:BluetoothServerSocket? = null
val mSocketType:String = if (secure) "Secure" else "Insecure"
init {
try {
mmServerSocket = if (secure){
mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_SECURE)
} else {
mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE, UUID_INSECURE)
}
}catch (ioe:IOException){
Log.e(TAG, "Socket Type: $mSocketType listen() failed", ioe)
}
mState = STATE_LISTEN
}
override fun run() {
Log.d(TAG, "Socket Type: $mSocketType BEGIN $this")
name = "AcceptThread $mSocketType"
var socket:BluetoothSocket? = null
while (mState!=STATE_CONNECTED){
try {
socket = mmServerSocket!!.accept()
}catch (ioe:IOException){
Log.e(TAG, "Socket Type: $mSocketType accept failed", ioe)
break
}
}
socket?.let {
synchronized(this#BluetoothChatService){
when(mState){
STATE_LISTEN -> {}
STATE_CONNECTING -> {
connected(socket!!, socket!!.remoteDevice, mSocketType)
}
STATE_NONE -> {}
STATE_CONNECTED -> {
try {
it.close()
}catch (ioe:IOException){
Log.e(TAG, "Could not close unwanted socket", ioe)
}
}
else->{throw BluetoothChatServiceException("Invalid Service State")}
}
}
}
Log.i(TAG, "End mAcceptThread, Socket Type: $mSocketType")
}
fun cancel(){
Log.d(TAG, "Socket Type: $mSocketType cancel $this")
try {
mmServerSocket!!.close()
}catch (ioe:IOException){
Log.e(TAG, "Socket Type: $mSocketType close() of server failed", ioe)
}
}
}
Please let me know if you're able to spot anything. I would really appreciate the help. Also, I should mention that the original code was in Java. I am writing mine in Kotlin.
I noticed that your Kotlin when blocks aren't replicating the function from the Java code.
Kotlin:
when (mState) {
STATE_LISTEN -> {}
STATE_CONNECTING -> {
connected(socket!!, socket!!.remoteDevice, mSocketType)
}
...
is not the Java code:
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice(), mSocketType);
break;
...
but is instead this:
switch (mState) {
case STATE_LISTEN:
break;
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice(), mSocketType);
break;
...
Your Kotlin code should be:
when (mState) {
STATE_LISTEN, STATE_CONNECTING -> {
connected(socket!!, socket!!.remoteDevice, mSocketType)
}
...
In Java the switch branch without the break falls through to the next line of code until the program hits a break while in Kotlin the entry with arrow/braces define a branch. Just double check all of your when statements to make sure they are following the expected Java switch behavior.
Turns out the problem was my while loop. The while(mState!=STATE_CONNECTED loop should have enclosed the rest of the run() function body. So the solution was just moving the curly brace. Sometimes it's the little things. But I do appreciate the help with my when expressions.
Related
I have difficulties writing an UDP message receive loop for Android.
In the following code, in receiveLoop, the call to receiveMessages never returns and I therefore never enter the message treatment loop.
Note that I am still able to receive packets, but it stops when the channel buffer is full.
I would expect receiveMessages to return immediately, while the blocking IO loop inside it would still run forever.
class MySocketUDP(private val params: SocketParams) {
private val rcvSocket: DatagramSocket by lazy {
val sock = DatagramSocket(params.rcvPort)
sock.reuseAddress = true
sock.soTimeout = 1000
sock
}
suspend fun receiveMessages(channel: SendChannel<Message>) {
withContext(Dispatchers.IO) {
val buf = ByteArray(MAX_MSG_SIZE)
while (true) {
val pkt = DatagramPacket(buf, buf.size)
try {
if (channel.isClosedForSend) {
break
}
rcvSocket.receive(pkt)
val msg = packetToMessage(buf, 0, pkt.length)
Log.d("SOCKET", "filling channel with $msg")
channel.send(msg)
} catch (ex: SocketTimeoutException) {
} catch (ex: CancellationException) {
break
}
}
}
}
}
class MyModel {
private suspend fun receiveLoop(socket: MySocketUDP) {
withContext(Dispatchers.Main) {
val channel = Channel<Message>(16)
socket.receiveMessages(channel)
Log.d("MODEL", "Entering msg loop")
for (msg in channel) {
dispatchRcvMessage(msg)
}
}
}
}
Why does receiveMessages never return while it is running in the IO dispatcher and called from the Main dispatcher?
Do I need to actually spawn a thread to such producer/consumer work?
Can you show how to achieve such long blocking code nicely in a "coroutine-friendly" manner?
Thank you
receiveMessages() is a suspend function which calls another suspend function withContext(), which in turn has an infinite loop. So calling socket.receiveMessages(channel) will suspend code execution while the loop is not finished.
You need to launch separate coroutines for consumer and producer, e.g. using launch function.
Some example of using coroutines:
val someScope = CoroutineScope(Dispatchers.Main)
private suspend fun receiveLoop(socket: MySocketUDP) = someScope.launch {
val channel = Channel<Message>(16)
socket.receiveMessages(channel)
Log.d("MODEL", "Entering msg loop")
for (msg in channel) {
dispatchRcvMessage(msg)
}
}
// In MySocketUDP
suspend fun receiveMessages(channel: SendChannel<Message>) {
someAnotherScope.launch { // or can use coroutineScope builder function
val buf = ByteArray(MAX_MSG_SIZE)
while (true) {
val pkt = DatagramPacket(buf, buf.size)
try {
if (channel.isClosedForSend) {
break
}
rcvSocket.receive(pkt)
val msg = packetToMessage(buf, 0, pkt.length)
Log.d("SOCKET", "filling channel with $msg")
channel.send(msg)
} catch (ex: SocketTimeoutException) {
} catch (ex: CancellationException) {
break
}
}
}
}
I notice that if i write fast and continuously a characteristic value the gatt server disconnect.
I know that I have to wait until onCharacteristicWrite callback, so that's not the problem I think.
This my queue implementation, I'm using a kotlin Channel to syncronize write and read.
private var continuation: CancellableContinuation<BluetoothGattCharacteristic>? = null
private val channel = Channel<WriteOp>(1)
private suspend fun processBluetoothWrite() {
do {
val writeOp = channel.receiveOrNull()
writeOp?.apply {
try {
suspendCancellableCoroutine<BluetoothGattCharacteristic> { cont ->
continuation = cont
characteristic.value = writeOp?.value
Log.d(TAG, "Write to ${characteristic?.uuid} value ${writeOp?.value?.toHexString()}...")
if (gatt?.writeCharacteristic(characteristic) == false) {
cont.resumeWithException(Exception("Write to ${characteristic?.uuid} fails."))
}
}
} catch (ex: Exception) {
Log.e(TAG, ex.message, ex)
}
}
} while (writeOp != null)
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?,
status: Int
) {
Log.d(TAG, "Write to ${characteristic?.uuid} value ${characteristic?.value?.toHexString()} | ${status}")
characteristic?.apply {
if (status == BluetoothGatt.GATT_SUCCESS) {
continuation?.resume(this)
} else {
continuation?.resumeWithException(Exception("Write to ${characteristic?.uuid} value ${characteristic?.value?.toHexString()} | ${status}"))
}
}
}
I need to add a delay of about 100ms in the queue processing to avoid disconnection.
UPDATE
After setting writeType as default, it seems that onCharacteristicWrite is more realistic (I used to get GATT_SUCCESS even when the device stopped communicating, so I guess it was a "virtual" state), now when the device stopped communicating it didn't get the onCharacteristicWrite callback, though after a while it is fired with status = 133.
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
What does it mean?
I'm making an android app for a school project, using Android Studio (Kotlin).
I need to send strings to an Arduino Genuino Uno module, passing by a HC-05 bluetooth module.
The Arduino will already be connected (paired) to my android device when the app will be launched.
Can someone help me to find a right and easy way to only SEND these datas ?
Thanks a lot.
I finally got the answer, I did that :
private fun CheckBt() {
Toast.makeText(applicationContext, "It has started", Toast.LENGTH_SHORT).show()
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (!mBluetoothAdapter.enable()) {
Toast.makeText(applicationContext, "Bluetooth Disabled !", Toast.LENGTH_SHORT).show()
/* It tests if the bluetooth is enabled or not, if not the app will show a message. */
finish()
}
if (mBluetoothAdapter == null) {
Toast.makeText(applicationContext, "Bluetooth null !", Toast.LENGTH_SHORT).show()
}
}
fun Connect() {
val device = mBluetoothAdapter.getRemoteDevice("98:D3:32:71:17:DE")
Log.d("", "Connecting to ... $device")
Toast.makeText(applicationContext, "Connecting to ... ${device.name} mac: ${device.uuids[0]} address: ${device.address}", Toast.LENGTH_LONG).show()
mBluetoothAdapter.cancelDiscovery()
try {
btSocket = device.createRfcommSocketToServiceRecord(myUUID)
/* Here is the part the connection is made, by asking the device to create a RfcommSocket (Unsecure socket I guess), It map a port for us or something like that */
btSocket.connect()
Log.d("", "Connection made.")
Toast.makeText(applicationContext, "Connection made.", Toast.LENGTH_SHORT).show()
} catch (e: IOException) {
try {
btSocket.close()
} catch (e2: IOException) {
Log.d("", "Unable to end the connection")
Toast.makeText(applicationContext, "Unable to end the connection", Toast.LENGTH_SHORT).show()
}
Log.d("", "Socket creation failed")
Toast.makeText(applicationContext, "Socket creation failed", Toast.LENGTH_SHORT).show()
}
//beginListenForData()
/* this is a method used to read what the Arduino says for example when you write Serial.print("Hello world.") in your Arduino code */
}
private fun writeData(data: String) {
var outStream = btSocket.outputStream
try {
outStream = btSocket.outputStream
} catch (e: IOException) {
//Log.d(FragmentActivity.TAG, "Bug BEFORE Sending stuff", e)
}
val msgBuffer = data.toByteArray()
try {
outStream.write(msgBuffer)
} catch (e: IOException) {
//Log.d(FragmentActivity.TAG, "Bug while sending stuff", e)
}
}
Hello everyone I am new to android development i am connecting with bluetooth device as a client in different class thread. If Nullpointerexcception occur then i use default UUID. After this when i use socket.connect() it show debug warning and don't send any pair request to device. Nothing happen. I am new to android development if any one can help. Thank you in advance.
Warning and Logs
here is my code in Thread;
class ConnectWithDevice(context : ConnectWithBluetooth, device : BluetoothDevice) : Thread(){
private val mContext : ConnectWithBluetooth = context
private val mmSocket : BluetoothSocket
private val mmDevice : BluetoothDevice
// Default UUID
private val mmDefaultUUID = UUID.fromString("78c374fd-f84d-4a9e-aa5b-9b0b6292952e")
init {
var temp : BluetoothSocket? = null
mmDevice = device
try {
temp = device.createRfcommSocketToServiceRecord(mmDevice.uuids[0].uuid)
}catch (en : NullPointerException){
en.printStackTrace()
temp = device.createRfcommSocketToServiceRecord(mmDefaultUUID)
}catch (e : IOException){
e.printStackTrace()
Log.e("TAG","Socket's create() method failed",e)
}
mmSocket = temp!!
Log.i("TAG","Got the Socket")
}
override fun run() {
// Cancel discovery because it otherwise slows down the connection.
if(mContext.bluetoothAdapter != null){
mContext.bluetoothAdapter!!.cancelDiscovery()
}
try{
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
Log.i("TAG","Connecting...")
mmSocket.connect()
Log.i("TAG","Bluetooth Successfully Connected")
}catch (connectException : IOException){
// Unable to connect; close the socket and return.
try{
mmSocket.close()
}catch (closeException : IOException){
Log.e("TAG","Could not close the client socket",closeException)
}
return
}
// The connection attempt succeeded. Perform work associated with
// the connection in a separate thread.
Log.i("TAG","Device is Connected")
//manageMyConnectedSocket(mmSocket)
}
// Closes the client socket and causes the thread to finish.
// Call this method from the main activity to shut down the connection.
fun cancel(){
try {
mmSocket.close()
} catch (e: IOException) {
Log.e(ContentValues.TAG, "Could not close the client socket", e)
}
}
}
Finally i find the solution here it is ->
I use this code ->
val m = device.javaClass.getMethod("createRfcommSocket", *arrayOf<Class<*>>(Int::class.java))
temp = m.invoke(device, 1) as BluetoothSocket
Instead of this ->
temp = device.createRfcommSocketToServiceRecord(mmDevice.uuids[0].uuid)
I am writing an Android app mostly in Kotlin that is supposed to scan for Bluetooth devices and also pair with them. I also want it to have a Bluetooth server socket running in the background to await connection attempts. However, I keep running into the same exception when attempting to invoke the BluetoothSocket.connect() method. The exception is:
10-10 20:07:57.917 18643-27894/com.example.zemcd.toofxchange E/Pairing Thread: error connecting
java.io.IOException: read failed, socket might closed or timeout, read ret: -1
at android.bluetooth.BluetoothSocket.readAll(BluetoothSocket.java:754)
at android.bluetooth.BluetoothSocket.readInt(BluetoothSocket.java:766)
at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:388)
at com.example.zemcd.toofxchange.PairingThread.run(BluetoothUtils.kt:83)
I read that this could be fixed with code similar to
btSocket = device.javaClass.getMethod("createRFcommSocket", Int::class).invoke(device, 1) as BluetoothSocket
But this does not work. It causes the app to crash with a ReflectException caused by NoSuchMethod. Also I have read that this is not a published method for a reason and I would like to try to use the published createRFcommSocketToServiceRecord() method. I am unsure of where to go from here, or what exactly is causing the IOException. Also, I never even get to the pairing screen. I am trying to find what is the cause of this exception, and how to fix it. My code:
class BluetoothUtils {
companion object {
val _UUID = UUID.fromString("a0e7e4c7-0e4e-43b7-9d18-659192512164")
val TAG = "BluetoothUtils"
fun initPairingServer(adapter: BluetoothAdapter){
var mmServerSocket: BluetoothServerSocket? = null
try {
var tmp = adapter.listenUsingRfcommWithServiceRecord(TAG, _UUID)
mmServerSocket = tmp
ListenThread(mmServerSocket).start()
}catch (ioe: IOException){
Log.e(TAG, "Error initializing Bluetooth", ioe)
}
}
fun pair(adapter: BluetoothAdapter, device: BluetoothDevice){
var btSocket: BluetoothSocket? = null
try {
adapter.cancelDiscovery()
btSocket = device.createRfcommSocketToServiceRecord(_UUID)
PairingThread(btSocket).start()
}catch (ioe: IOException){
Log.e(TAG, "error connecting", ioe)
}
}
}
}
class ListenThread(val btServSock: BluetoothServerSocket) : Thread(){
companion object {
val TAG = "ListenThread"
}
var btSocket: BluetoothSocket? = null
override fun run() {
super.run()
while (true){
try {
Log.d(TAG, "listening . . . ")
btSocket = btServSock.accept()
}catch (ioe: IOException){
Log.e(TAG, "Error", ioe)
break
}
//manage connection here
//with either BluetoothUtils function
//or BluetoothSocket extension
}
}
}
class PairingThread(val btSocket: BluetoothSocket) : Thread(){
companion object {
val TAG = "Pairing Thread"
}
override fun run() {
super.run()
try {
Log.d(TAG, "attempting to connect")
btSocket.connect()
}catch (ioe: IOException){
Log.e(TAG, "error connecting", ioe)
btSocket.close()
}
}
}
Please somebody help me find my problem. Could it be that I'm attempting to connect to a device that isn't using the same UUID? I am just trying to connect to my laptop.