Search code examples
javaandroid-studiobeacon

Beacon and Eddystone detection issue


The problem consist of detecting the IBeacon and Eddystone Frames, when I have the Eddystone I have to use specific function and the same for the IBeacon. I am using Java in Android Studio so the function who deliver me the IBeacon result is getManufatureSpecificData() and for the Eddystone is getServiceData(). The question is why there is different function to have the result? and how can I check if the received frame is Eddystone or IBeacon?

The snippet of code

    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
        beaconManager.clean();
        Log.i(TAG, "On batch called, size of result is : " + results.size());

        for (int index = 0; index < results.size(); ++index) {
            try {
                ScanRecord mScanRecord = results.get(index).getScanRecord();
                Map<ParcelUuid, byte[]> myMap = mScanRecord.getServiceData();
                int mRsi = results.get(index).getRssi();
                String url = "";
                byte[] txPower = new byte[1];
                byte[] nameSpaceId = new byte[10];
                byte[] instanceId = new byte[6];
                float distance;
                
                for (Map.Entry<ParcelUuid, byte[]> eddystoneFrame : myMap.entrySet()) {
                    // for eddystone URL
                    if (eddystoneFrame.getValue()[0] == 16) {
                        url += urlSchemePrefix[eddystoneFrame.getValue()[2]];

                        for (int i = 3; i < eddystoneFrame.getValue().length - 1; i++) {
                            url += (char) eddystoneFrame.getValue()[i];
                        }

                        url += topLevelDomain[eddystoneFrame.getValue()[eddystoneFrame.getValue().length - 1]];
                        txPower[0] = eddystoneFrame.getValue()[1];
                        distance = (float) Utils.calculateDistance((int) txPower[0], (double) mRsi);
                        
                        try {
                            beaconManager.addEddyBeacon(Utils.bytesToHex(nameSpaceId), Utils.bytesToHex(instanceId), distance);
                        } catch (Exception e) {
                            Log.e(TAG, e.toString());
                        }

                    }
                    else if (eddystoneFrame.getValue()[0] == 0) {
                        // For Eddystone UID
                        System.arraycopy(eddystoneFrame.getValue(), 2, nameSpaceId, 0, nameSpaceId.length);
                        System.arraycopy(eddystoneFrame.getValue(), 12, instanceId, 0, instanceId.length);
                        System.arraycopy(eddystoneFrame.getValue(), 1, txPower, 0, txPower.length);

                        distance = (float) Utils.calculateDistance((int) txPower[0], (double) mRsi);

                        /**  beaconList.add(new String[]{"Name Space ID : " + Utils.bytesToHex(nameSpaceId)+ "\n" + "Instance ID :" + Utils.bytesToHex(instanceId),
                         String.format("%.2f", distance),
                         "eddystoneuid"});**/
                        try {
                            beaconManager.addEddyBeacon(Utils.bytesToHex(nameSpaceId), Utils.bytesToHex(instanceId), distance);
                        } catch (Exception e) {
                            Log.e(TAG, e.toString());
                        }

                    }
                    Log.i(TAG, "The size of the frame is: " + eddystoneFrame.getValue().length);
                }


            } catch (Exception e) {
                Log.e("Error123456789", e.toString());
                break;
            }
        }

Solution

  • There are two variants of Bluetooth LE advertisements defined by the Bluetooth SIG standards body:

    1. GATT Service Advertisements - these are designed to advertise GATT Services and may have service data bytes, which are designed to tell you something about the advertised service.
    2. Manufacturer Advertisements - these are designed for any purpose that the Bluetooth device manufacturer wishes to use. They always include manufacturer data of 0-24 bytes.

    When Apple designed the iBeacon format, it chose to use manufacturer advertisements. As a result, whenever you detect them you must get the manufacturer data to decode the contents.

    When Google designed Eddystone, it chose not to use manufacturer advertisements and instead use GATT Service advertisments. It made this choice because it wanted he format to work well on iOS devices, and iOS generally refuses to let 3rd party apps detect manufacturer advertisements (other than iBeacon) in the background.

    Because of this design decision by Google, you must check to see if there is service data when attempting to decode Eddystone advertisements.

    If you want to detect both beacon types, use logic like this:

    1. Is there attached service data? If no, go to step 3.
    2. Can the service data be decoded as Eddystone? If yes, we are done.
    3. Is the advert a manufacturer advert? If no, go to step 5.
    4. Can the manufacturer data be decoded at iOS, if yes, we are done.
    5. The advertisement is neither Eddystone nor iBeacon.