What triggers the BluetoothDevice.ACTION_ACL broadcasts?

Tombola picture Tombola · Mar 2, 2012 · Viewed 14.3k times · Source

I would like to know what events in remote physical devices trigger ACTION_ACL_CONNECTED and ACTION_ACL_DISCONNECTED in a listening device. My test results make no sense. I have gathered several devices within a few decimeters of each other:

  • a Galaxy Tab P7500 running Android 3.1
  • an i5500 phone running Android 2.2
  • a WinXP PC with a bluetooth USB dongle
  • two headsets with on/off buttons

First, I pair manually with all devices from the Tab. Neither the PC nor the phone are paired with any other device but the Tab. (One of the headsets can never be found by the tab in any way, but it can easily be found from the phone both manually and programmatically). Then I have a simple app to start discovery and which listens to and displays the ACL broadcasts. And this is what happens (same thing every time, it's consistent in its madness):

  • startDiscovery() from Tab with all devices enabled: - The PC is the only device found
  • Disable bluetooth on PC: - No reaction on the Tab
  • Enable bluetooth on PC: - No reaction on the Tab
  • Power on headset 1st time: - ACTION_ACL_CONNECTED on the Tab
  • Power off headset: - No reaction on the Tab
  • Power on headset again: - ACTION_ACL_DISCONNECTED and ACTION_ACL_CONNECTED in quick succession on the Tab
  • Disable bluetooth on Tab: - No reaction on the Tab
  • Enable bluetooth on Tab: - Headset ACTION_ACL_CONNECTED on the Tab
  • startDiscovery() from phone: - The PC is the only device found by the phone, although the phone is only paired with the Tab, not with the PC. Otherwise, the phone only reacts to the headset which the Tab never reacts on.

What to make out of this mess? Can't one rely on a device causing an ACTION_ACL_CONNECT even when it is paired and powers up within range?

Here are the methods for BroadcastReceiver and the activities onCreate, but I don't expect details in this code to matter:

BroadcastReceiver intentReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (device != null) {
            name = device.getName();
        Log.v(TAG, "Device=" + device.getName());
        }
        else {
            name = "None";
        }

        if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
            text1.setText(name + " connected " + (checkCounter++));
            Log.v(TAG, "connected: " + device);
        }
        else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
            text2.setText(name + " disconnected " + (checkCounter++));
        Log.v(TAG, "disconnected: " + device);
        }
        else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            text3.setText( name + " found " + (checkCounter++));
        Log.v(TAG, "found: " + device + "");
        }
        else if (blueAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
            text4.setText("Started " + (checkCounter++));
            Log.v(TAG, "Discovery started");
        }
        else if (blueAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            text4.setText("Finished " + (checkCounter++));
            Log.v(TAG, "Discovery finished");
        }
    }
};


public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.bluetoothlayout);

    text1 = (TextView)findViewById(R.id.textView1);
    text2 = (TextView)findViewById(R.id.textView2);
    text3 = (TextView)findViewById(R.id.textView3);
    text4 = (TextView)findViewById(R.id.textView4);

    BluetoothDevice mw600 =         blueAdapter.getRemoteDevice("58:17:0C:EB:C5:08");
    BluetoothDevice bt500 =         blueAdapter.getRemoteDevice("00:1D:43:00:C4:54");
    BluetoothDevice galaxyTab = blueAdapter.getRemoteDevice("00:07:AB:6A:96:7D");
    BluetoothDevice pcDongle =  blueAdapter.getRemoteDevice("00:15:83:4D:8B:57");

    intentFilter = new IntentFilter();
    intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
    intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
    intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    intentFilter.addAction(blueAdapter.ACTION_DISCOVERY_STARTED);
    intentFilter.addAction(blueAdapter.ACTION_DISCOVERY_FINISHED);
    if (!isReceiverRegistered) {
        registerReceiver(intentReceiver, intentFilter);
        isReceiverRegistered = true;
    }
    if (!blueAdapter.isEnabled()) {
        blueAdapter.enable();
    }
    blueAdapter.startDiscovery();
}

Answer

NikkyD picture NikkyD · Oct 3, 2012

I'm working with androids really messed up bt for quite some time now,

here is what i can tell you:

ACTION_ACL_CONNECTED is sent whenever a successful connection was established. This one is as simple as it gets.

Now the rather annoying part.

ACTION_ACL_DISCONNECTED is sent whenever the connection was closed on a HARDWARE level. WHEN that happens is a bit up to the device itself. IF you manually disconnect/plug out the other device, it somehow does not send a "dude, im gone" signal to the droid, instead after up to 20 seconds some watchdog barks and the connection is being closed and the intent is sent.

Now i did try this only with SPP devices that I connect to. A headset afaik is actively connecting by itself because its not SPP. So it automatically connects to you, if you are paired and in listening mode. Now i dont know what the headset does if you "power it off". Maybe it disconnects properly or maybe it just disrupts the connection without saying goodbye. In the latter case it would take some time for the watchdog to disconnect from the droid side, which can take from 0 to 20 seconds, dont ask me why, its just my observation.