Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reacting to BLE directed advertising (ADV_DIRECT_IND) in Android

How to react to directed advertising (ADV_DIRECT_IND == 0001) in Android?

There is a BLE-gadget which sends directed advertising to an Android phone (using hardcoded MAC address of the phone for now) and in my Android app I would like to react and to initiate a connection to the gadget and read the org.bluetooth.characteristic.location_and_speed value from the gadget:

screenshot

Please advise if it's possible by the means of Android 5 API.

like image 220
Alexander Farber Avatar asked Aug 07 '15 14:08

Alexander Farber


2 Answers

Direct advertising does work - at least with HTC M8 phone and Android 5.0 (API level 21 and higher).

The solution has been to add the device addresses to the ScanFilter.

If you leave the filters empty, the scanning callbacks won't be called.

Here my working code:

public static final String TAG = "My_BLE_app";

public static final String DEVICE_1 = "D4:BE:84:72:5B:8E";
public static final String DEVICE_2 = "C4:39:07:19:60:E2";

private Context mContext;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mScanner;
private ScanSettings mSettings;
private List<ScanFilter> mFilters = new ArrayList<ScanFilter>();

First the method for initializing BLE:

public boolean init() {
    BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
    if (mBluetoothAdapter == null)
        return false;

    mScanner = mBluetoothAdapter.getBluetoothLeScanner();
    mSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();

    mFilters.add(new ScanFilter.Builder().setDeviceAddress(DEVICE_1).build());
    mFilters.add(new ScanFilter.Builder().setDeviceAddress(DEVICE_2).build());

    return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}

And then the scanning callbacks:

private ScanCallback mDirectedScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        processResult(result);
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        for (ScanResult result: results) {
            processResult(result);
        }
    }

    private void processResult(ScanResult result) {
        BluetoothDevice device = result.getDevice();
        if (device == null)
            return;

        String address = device.getAddress();
        if (!BluetoothAdapter.checkBluetoothAddress(address))
            return;

        int rssi = result.getRssi();

        Log.d(TAG, "address=" + address + ", rssi=" + rssi);

        // TODO connect to the device in under 1 second
    }
};

Finally the code to start the scanning:

mScanner.startScan(mFilters, mSettings, mDirectedScanCallback);

One question is still open though:

I don't know how to detect the type of scan at the Android side - that is if the scan was directed or not.

like image 59
Alexander Farber Avatar answered Nov 17 '22 11:11

Alexander Farber


To partially answer the open question:
I've noticed that the ScanRecord.getBytes() is empty - all '\0' - for a directed scan (ADV_DIRECT_IND) - but would contain the advertising data otherwise (ADV_IND).

like image 23
Kirk Bates Avatar answered Nov 17 '22 10:11

Kirk Bates