32

A Bluetooth low energy device is uniquely identified by it's address (in the Android API they call this the MAC address and denote it as colon separated hex values e.g. 11:aa:22:bb:33:cc).

But to uniquely identify a BLE address you need to know if it's a public or a private address. In essence, 49 bits are necessary to identify an address, not 48.

Random addresses can be either static random, non-resolvable private or resolvable private and these types are separated by a bit pattern in the two most significant bytes (11, 00 and 10 respectively).

But I don't see anywhere that you can separate public and random addresses just by looking at the 48 bits in the address.

So how does this work in the Android API? How do they know what device to connect to when they don't know if the address you've specified are public or random?

The API in question is for instance the getRemoteDevice function. It says:

Valid Bluetooth hardware addresses must be upper case, in a format such as
"00:11:22:33:AA:BB". The helper checkBluetoothAddress(String) is available
to validate a Bluetooth address.

A BluetoothDevice will always be returned for a valid hardware address,
even if this adapter has never seen that device.

So you give the function 48 bits of data and there is no way to tell it if the address is public or private. This means the device is not uniquely identified.

Vegar Westerlund
  • 1,604
  • 3
  • 16
  • 24
  • The public address is built up of 24 bits of company assigned id (LSB) and a company id (MSB). The list of company ID's can be found here[1]. From the list it seems that any combination of the two most significant bits are allowed. [1] http://standards.ieee.org/develop/regauth/oui/oui.txt – Vegar Westerlund May 05 '14 at 11:30
  • 1
    Are you sure they are 49 bits? its too ugly a number of bits for an address. – Mister Smith May 12 '14 at 09:10
  • The address is 48 bits (of course), but you also need to now whether that address is a public or a private address, so one bit extra... :-). I updated the question to be more clear about this. – Vegar Westerlund May 12 '14 at 09:35
  • [Here](http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#find) is how you do it. Basically you start a scan, and you receive the device info in the callback. – Mister Smith May 12 '14 at 09:48
  • @MisterSmith: I understand that is how you do device discovery. I'm just really wondering about the API. It's seems to be missing a very important part of the BLE core specification. This might be because the Android Bluetooth API is used both for ER/EDR and LE. But still... – Vegar Westerlund May 12 '14 at 10:41
  • @MisterSmith Also, the info you get in the scan callback does not tell you if the address of the device is private or public so even the scan API has the same issue. – Vegar Westerlund May 12 '14 at 10:49
  • In android, given the way the API is designed, you are supposed to subscribe to a BLE device using a `BluetoothDevice` instance (obtained either scanning or from the list of paired devices), and from there on you just receive callbacks. The MAC can change, but I guess the new MAC could be obtained through [`BluetoothGatt.getDevice`](http://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getDevice), but it won't be useful to identify the device from it. – Mister Smith May 12 '14 at 10:58
  • If you really need to, have a look at the [Device Information Service](https://developer.bluetooth.org/TechnologyOverview/Pages/DIS.aspx) that, if supported, gives you access to serial and model numbers. – Mister Smith May 12 '14 at 10:58
  • There is a lot of interesting information in the Device Information Service, non of which helps any with the question of what type of address a device is using. A device can use both types of addresses and does not need to specify which in the that service. – Vegar Westerlund May 12 '14 at 11:07
  • 1
    I think the API does not provide that info. You just should assume the MAC can change. About how the resolution is implemented, I think you might have to browse the source repos. – Mister Smith May 12 '14 at 11:18

6 Answers6

15

Since nobody else seems to have an answer to offer I started testing on my own.

I tried making an app that creates a device from a string representation of an address and tried setting up my device with the 48 bit address alternating the public or private bit to see what the Android stack does.

private final BluetoothGattCallback leGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            Log.i("Fisken", "Gatt connected " + gatt.getDevice().getAddress() + " status " + status);
            if (status != BluetoothGatt.GATT_SUCCESS) {
                Log.w("Fisken", "Disconnect and close");
                gatt.disconnect();
                gatt.close();
            }
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            Log.i("Fisken", "Gatt disconnected " + gatt.getDevice().getAddress() + " status " + status);
            if (status != BluetoothGatt.GATT_SUCCESS) {
                Log.w("Fisken", "Disconnect and close");
                gatt.disconnect();
            }
            gatt.close();
        }
    }
};

BluetoothAdapter mBluetoothAdapter = ((BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
BluetoothDevice d = mBluetoothAdapter.getRemoteDevice("FF:55:44:33:22:11");
d.connectGatt(this, false, leGattCallback);

With this code, if I start my BLE peripheral with a random address everything works as expected. However, if I try running it with the same address with the public bit set, logcat says "Gatt connected", but that's just not true. And I'm never able to disconnect.

Update: I did some more testing to figure this out. The onConnectionStateChange event I get is just the connection attempt timing out. The status is set to either 133 (if I get STATE_CONNECTED) or 257 (if I get a STATE_DISCONNECTED) and I've seen both. In either case I should (and now do in the sample code) cancel the connection attempt and close the client.

I've also found out that if I do a scan first, so that the device I'm trying to connect to have been seen recently and then do a connect based solely on the device mac address then I am able to connect to both random and public addresses without any trouble.

So this seems to be a bug/and or missing feature in the Android API. It does not allow you to connect to a public address without first having scanned for it. It does however work for random addresses.

Vegar Westerlund
  • 1,604
  • 3
  • 16
  • 24
  • Have you found a way to fetch the private adress and connect to it? I have the same issue with my device that changes its public mac address every 15 minutes for security reasons. That said, I cannot connect with the previously saved public mac address. – Elyess Abouda Mar 02 '15 at 16:06
  • 1
    @Vegar: What Android API would you expect to use to connect directly to the device without scanning for it first? If the device is paired and advertising one is able to connect directly. The auto-connect option I have found to be unreliable. – Brian Reinhold Oct 26 '15 at 11:19
  • Has anyone found where in the Android source the address resolution happens? Where does it determine it's guess for the address type? – PICyourBrain Sep 21 '18 at 01:02
1

It is possible to guess if the address is public or random, though it will not work in every case.

As you say above, in case of a random address, both MSB are either 00xx, 01xx or 11xx... so if it is 10xx, then it is a public address (from a company whose OUI starts with 8,9, A or B)

Also, the number of registered OUI is very limited compared to what is existing, so by searching the potential OUI in the IEEE database, a matching result will probably mean a public adress.

Registered OUI count: ~20500, so 0.12% out of 2^24 bits and 0.48% out of 2^22 bits.

Without the IEEE database, it is possible to rely on the fact that the first LSB of a OUI is always 0, and the second LSB is almost always 0 (actually, it should be always 0 as these addresses are universally administered).

Also, other statiscal analysis can be used: for instance, 60% of the OUI start with 00. On the other hand, a non resolvable private address, has only a probability of 1.66% to start with 00 (with uniform random generator).

calandoa
  • 5,668
  • 2
  • 28
  • 25
1

I think your original 'need 49 bits to distinguish between public and random addresses' is correct. I cannot find anything in the encoding of an IEEE public address which restricts the MSB to be '10' which, if true, would solve the problem.

So the only thing one can use is the 'random address' bit setting in the advertisements of the peripheral or the equivalent bit setting in the connection initiation packet of the central. If these bits are not set, then the address the said endpoint exposes is public.

I will add: From core spec Vol 6 Part B section 1.3 Device addresses: Calling MS = most significant

Static random address:          two MB bits of MS byte are 1 1 such that the MS byte is 11xxxxxx & with 0xC0
Non-resolvable private address: two MB bits of MS byte are 0 0 such that the MS byte is 00xxxxxx & with 0x00
Resolvable private address:     two MB bits of MS byte are 0 1 such that the MS byte is 01xxxxxx & with 0x40

There is no way to distinguish a public address from one of the above types of addresses without also having the address type flag. Thus the need for the '49th' bit (the extra flag). The address alone does not do it!

Brian Reinhold
  • 2,313
  • 3
  • 27
  • 46
  • I probably should have added that once you know that the address is a random type, then you can look at the two most significant bits to see what type of random address it might be, static, resolvable, etc. – Brian Reinhold Oct 11 '17 at 09:35
0

Whether the advertising address is public or private is set in the header of the advertising message. Setting the address type in the application layer to public means that the BLE link layer will transmit the actual "MAC"address. Setting the address type to static/private resolvable, indicates that the BLE link layer will scramble the address with an identity resolving key (IRK).

Freddy
  • 1
  • 1
0

You can differentiate between public and private addresses by looking at the 2 most significant bits. An address is 48 bits long, not 49. See Core Bluetooth Specification v4.2, Vol 6, Part B, Section 1.3.

  • 4
    Wrong. You can differentiate between the three different _random_ addresses (static random, non-resolvable private or resolvable private), by looking at the 2 most significant bits. But if it's a _public_ device address there is no restriction on the 2 most significant bits. See Core Bluetooth Specification v4.2, Vol 6, Part B, Section 1.3. This is the whole point of the question. – Vegar Westerlund Aug 26 '16 at 11:27
  • 1
    No, I don't think that's wrong. A bluetooth device address is always 48 bits long. It's probably true that a MA-L (aka OUI) from ieee has no restriction on the 2 most significant bits, so it may actually be a problem, in theory. I assume that Bluetooth SIG has assumed that ieee MA-L are increasing by 1 and that the number of ieee MA-L will never reach so high (2^46 is a pretty high number). Hence you can in practice differate between the types of addresses. I suggest you to ask Bluetooth SIG direcly: https://bluetooth.service-now.com/ess/ – speedycat Aug 30 '16 at 10:45
  • 1
    Take the public address I found close to home which starts with the Google Inc, OUI: 54:60:09. If you didn't know it was a public address you might assume it's resolvable private address. So it is actually a problem... Also, if you try BluetoothDevice.getRemoteDevice(...).connectGatt(...) with the address I found Android will not be able to connect. Because I know for a fact it's trying to reach the device with the same resolvable private address. – Vegar Westerlund Sep 11 '16 at 22:25
  • Maybe its a problem with the Android API for BLE. I don't like it at all, it looks poorly implemented and documented. – speedycat Oct 03 '16 at 12:25
  • Vegar is correct. You need 49 bits. The TxAd and RxAd bits in the PDU header is needed to know the type of address. As far as I can tell, Android provides no means to obtain this data, so I hope it handles it correctly in the lower layers! – Brian Reinhold Aug 22 '18 at 19:15
0

This seems too obvious for everyone to have missed but from what I have seen the PDU Hdr bytes contain a TxAdd bit that indicates whether or not the MAC is public or private...

Rex Brown
  • 43
  • 1
  • 7