David O'Meara
David O'Meara

Reputation: 3033

Unable to accept incoming Bluetooth connection in Android

Maybe similar to the unanswered thread here, but we have an Android application (tested on multiple handsets and multiple Android 2.1+ versions) which needs to listen for and accept connections from a remote bluetooth device. The remote device is a digital pen, but the main point is that the pen is paired with the phone and then sends data via SPP, which uses OBEX, which uses RFComm so all that should be fine.

Currently the application works by allowing the Android device to receive the OBEX payload and then get the app to look in the bluetooth folder to pick up the payload, but we want the application to be able to talk directly to the remote device. Keep in mind the remote connects to the android phone, the phone does not connect to the pen.

Our test code is based on the sample BluetoothChat application available in the Android samples, but essentially adapter.listenUsingRfcommWithServiceRecord never gets called and the best that we see in the Motorola Defy+ DDMS logs is:

INFO/BtOppRfcommListener(2577): Accepted connectoin from 00:07:CF:55:94:FB
INFO/BtOpp Service(2577): Start Obex Server
DEBUG/Obex ServerSession(2577): java.io.IOException: Software caused connection abort

This appears to show that the connection is accepted by Android but not made available to the application. The UUID used is the same UUID used in the JME version of the same application and was provided by the pen supplier.

Upvotes: 0

Views: 2763

Answers (2)

David O'Meara
David O'Meara

Reputation: 3033

In our case the only solution we found which could reliably accept Bluetooth OPP data from a remote device was to use the 'hidden' Android API. If you check the Android source, there are additional methods defined which are annotated @hide which excludes them from the exported Android jar and the API docs.

Using these methods should be avoided if possible and are not guaranteed to be consistent across Android versions.

There are two ways to include these in your code:

  1. use reflection
  2. include an additional development JAR at compile time which exposes these methods, but make sure this JAR is not packaged with your application.

We opted for #1 as our requirements were minimal.

Access the Method

Class<?>[] args = new Class[] { int.class };
Method listenMethod = BluetoothAdapter.class.getMethod(listenMethod, args);

Where the method name is one of (in order of preference) listenUsingEncryptedRfcommOn, listenUsingRfcommOn or listenUsingUnencrytptedRfcommOn

Create a server socket

BluetoothAdapter target = *your adapter, probably the default one*;
BluetoothServerSocket sock = (BluetoothServerSocket) (listenMethod.invoke(target, new Object[] { channel }));

Where in our case the OPP channel is 12

Fin

Provided you manage to get a BluetoothServerSocket back (exception handling is omitted, your mileage may vary, user discretion advised, no warrantee provided etc etc) you should be able to use it to listen for incoming OPP data.

Upvotes: 1

Sourab Sharma
Sourab Sharma

Reputation: 2960

The most common mistake we android developers do while copying BluetoothChat app code is we don't follow the flow of that app.

The common errors while implementing this code, like "Service Discovery Failed", "Software caused connection abort", "Connection aborted by peer", "Unable to start Service Discovery" , "Connection Lost" is the outcome of neglecting the flow.

I have faced all these issues while implementing the BluetoothChat code in my app and the solution was to correct the flow.

For example in my case :

        OnCreate:

            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        OnStart:

        if(this.mChatService == null)
                this.mChatService = new BluetoothChatService(this, mHandler);

        OnResume:
        if ((mChatService != null) &&  (mBluetoothAdapter.isEnabled())) {
    // Only if the state is STATE_NONE, do we know that we haven't started already
            if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
                        // Start the Bluetooth chat services
                        mChatService.start();
                    }
                }
        OnDestroy:
        if(mTransferService != null) mTransferService.stop();

   onActivityResult:
    case REQUEST_CONNECT_DEVICE:
    // When DeviceListActivity returns with a device to connect
    if (resultCode == Activity.RESULT_OK) {
    if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
            // Start the Bluetooth chat services
            mChatService.start();
            }
    if(mChatService .getState() != BluetoothChatService.STATE_CONNECTED){
    String address = data.getExtras().getString(ViewContactList.EXTRA_DEVICE_ADDRESS);
        // Get the BLuetoothDevice object
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        device = mBluetoothAdapter.getRemoteDevice(address);
        // Attempt to connect to the device
        mChatService.connect(device);
        }
        else{
        if(messageString.length() > 0)
        MyCurrentActivity.this.sendMessage(messageString);
        }

In simple words, implement the code in such a way both devices are in mState = STATE_LISTEN , before you will call

mChatService.connect(device);

Upvotes: 1

Related Questions