0

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.

Mox_z
  • 501
  • 7
  • 30
  • Assuming you are basing this off the Google Sample Bluetooth Chat, what does your `AcceptThread` look like. – Morrison Chang Nov 09 '17 at 06:33
  • @MorrisonChang I posted the code for AcceptThread in an edit just now, and yes, this is based off of the google sample. – Mox_z Nov 09 '17 at 06:44

2 Answers2

3

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.

Morrison Chang
  • 11,691
  • 3
  • 41
  • 77
  • I made the change that you suggested but i am still not seeing any difference. Could it be something to do with not receiving an allow device to connect prompt? Should i be seeing that? – Mox_z Nov 09 '17 at 07:36
  • If you are just rewriting the Bluetooth Chat app in Kotlin, the prompt behavior should be the same. You may want to disable "instant run" and/or delete the app just to be sure. One approach would be to go line by line between the Java and Kotlin versions to see if other differences similar to the `when` branch issue in your Kotlin code. Another would be to rerun the Java version with more logging and then repeating the same steps with the Kotlin version. With enough logging some difference should show up which would point to the error. – Morrison Chang Nov 09 '17 at 07:44
0

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.

Mox_z
  • 501
  • 7
  • 30