Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BLE Advertisement in Android

I am developing and app to Send BLE Advertisement packet in android. I have use AdvertiseData and AdverstiseSettings classes to generate the advertise packet. But when i do the StartAdvertising it always gives me an error with Error Code "2" , "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS", "Failed to start advertising because no advertising instance is available."

Below is my code for MainActivity.JAVA

package rockwellcollins.blutooth_advertise;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.os.Bundle;
import android.os.ParcelUuid;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {
    private BluetoothLeScanner mBluetoothLeScanner;
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        textView = (TextView) findViewById(R.id.txtv);
        mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();

        if( !BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported() ) {
            Toast.makeText(this, "Multiple advertisement not supported", Toast.LENGTH_SHORT).show();
        }
        advertise();
        BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner().startScan(scanCallback);
    }

    private void advertise() {
        BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();

        AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY )
                .setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_HIGH )
                .setConnectable(false)
                .build();
        Log.i("BLE","start of advertise data after settings");
        ParcelUuid pUuid = new ParcelUuid( UUID.fromString("b161c53c-0715-11e6-b512-3e1d05defe78"));

        AdvertiseData data = new AdvertiseData.Builder()
                .setIncludeDeviceName( true )
                .setIncludeTxPowerLevel(true)
                .addServiceUuid( pUuid )
                //.addServiceData( pUuid, "Data".getBytes(Charset.forName("UTF-8") ) )
                .build();
        Log.i("BLE","before callback");
        AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
            @Override
            public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                super.onStartSuccess(settingsInEffect);
                Log.i("BLE", "LE Advertise success.");

            }

            @Override
            public void onStartFailure(int errorCode) {
                Log.e("BLE", "Advertising onStartFailure: " + errorCode);
                super.onStartFailure(errorCode);
            }
        };

        advertiser.startAdvertising( settings, data, advertisingCallback );
        Log.i("BLE", "start advertising");
    }

    private final ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            printScanResult(result);
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            textView.append("Received " + results.size() + " batch results:\n");
            for (ScanResult r : results) {
                printScanResult(r);
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            switch (errorCode) {
                case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
                    textView.append("Scan failed: already started.\n");
                    break;
                case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
                    textView.append("Scan failed: app registration failed.\n");
                    break;
                case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED:
                    textView.append("Scan failed: feature unsupported.\n");
                    break;
                case ScanCallback.SCAN_FAILED_INTERNAL_ERROR:
                    textView.append("Scan failed: internal error.\n");
                    break;
            }
        }

        private void printScanResult(ScanResult result) {
            String id = result.getDevice() != null ? result.getDevice().getAddress() : "unknown";
            int tx = result.getScanRecord() != null ? result.getScanRecord().getTxPowerLevel() : 0;
            textView.append("TX: " + tx + " RX: " + result.getRssi() + " from " + id+ ".\n");
        }
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Code for Android Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="rockwellcollins.blutooth_advertise">
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Could you please let me know what I am doing wrong and how can I solve this error?

Thanks

like image 317
MMJ Avatar asked Apr 20 '16 17:04

MMJ


3 Answers

From my experience there are 4 types of Android devices in regard BLE advertisement:

  1. Devices with Android pre-5.0 - LE Advertisement not supported
  2. Devices with Android 5+ that don't support LE Advertisement and return null from getBluetoothLeAdvertiser(). Those devices return false from isMultipleAdvertisementSupported(). They do this even with Bluetooth ON (see Note below).
  3. Devices with Android 5+ that return the BluetoothLeAdvertiser object, but each try of advertising ends with ADVERTISE_FAILED_TOO_MANY_ADVERTISERS error (this is the case you have). Those devices return true from isMultipleAdvertisementSupported() which as you see is not true. So far I've seen only one phone from this category: Sony xperia z1 compact, but if there is one, there are more.
  4. Devices with Android 5+ that support LE Advertisement. Those return true from isMultipleAdvertisementSupported() but ONLY when Bluetooth is ON.

Note: in the 2., 3. and 4. the BluetoothLeAdvertiser object is returned ONLY when Bluetooth is ON. Otherwise null is returned, so you actually have no clue whether the device supports LE Advertisement or not until Bluetooth is enabled.

Check the nRF Connect app: Disable Bluetooth, install the app, open and select Advertiser tab or Navigation menu -> Device information. It will ask you to turn Bluetooth ON before the status will be shown.

like image 88
philips77 Avatar answered Oct 11 '22 08:10

philips77


See this question for a possible answer, BLE Advertisments are not supported on every device.

Also try to omit the device name as suggested here.

like image 39
Jonas Köritz Avatar answered Oct 11 '22 07:10

Jonas Köritz


You only need to add this code: @TargetApi(Build.VERSION_CODES.M) over your method

like image 26
jessyavalos1615 Avatar answered Oct 11 '22 07:10

jessyavalos1615