6

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

MMJ
  • 519
  • 1
  • 9
  • 26

3 Answers3

9

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.

philips77
  • 1,294
  • 14
  • 24
  • 1
    I'm currently testing on a device that does not fall in any of those types, Huawei Mate 20 lite: isMultipleAdvertisementSupported() returns false even though it does allow advertisement, but only one at a time (hence it is sort of correct, as it does not allow multiple). – Jorge Galvão Apr 05 '20 at 16:28
  • @JorgeGalvão I'm facing the same problem with an Honor 10 updated to Android 10. How do you start advertising if the device has advertise feature, but no multiple advertisement feature? – giuseppe_para Sep 07 '20 at 14:31
  • 1
    @giuseppe_para sorry for missing your question until now... I don't think I ever worked on solving that issue, because I dropped the project. I think that I wasn't even handling the case where the `getBluetoothLeAdvertiser()` would return null. As far as I see looking at my code, I just tested for the `getSystemService(Context.BLUETOOTH_SERVICE)` and it's `getAdapter()` for null. Then I also checked the `onStartFailure` callback of the `AdvertiseCallback`. – Jorge Galvão Jan 15 '21 at 16:20
  • 1
    @giuseppe_para You can check the ForegroundService.java and the BluetoothLeAdvertiseHelperLollipop.java here (it was working overall, but I never tested against many devices): https://github.com/Track-COVID-19/Track-COVID-19-Android/tree/develop/app/src/main/java/pt/gaps/epidemictracker – Jorge Galvão Jan 15 '21 at 16:22
  • Thanks! I'm not working anymore on this project, but I'll try this solution when I'll work again on it. – giuseppe_para Jan 19 '21 at 16:46
2

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.

Community
  • 1
  • 1
Jonas Köritz
  • 2,606
  • 21
  • 33
  • I check for the Bluetooth LE Feature and it's available on my device. `PackageManager manager = getApplicationContext().getPackageManager(); boolean featureAvailable = manager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); if(!featureAvailable) Toast.makeText(this, "Bluetooth LE feature is not available", Toast.LENGTH_SHORT).show();` – MMJ Apr 20 '16 at 18:20
  • 2
    Just the Bluetooth LE feature isn't enough. The advertisement is not supported on all devices that have Bluetooth LE. There's a [list of compatible devices](https://altbeacon.github.io/android-beacon-library/beacon-transmitter-devices.html) and you test if your device can advertise if you try out [this app from Radius](https://play.google.com/store/apps/details?id=com.radiusnetworks.locate). – Markus Kauppinen Apr 20 '16 at 18:31
  • Actually, I am using Qualcomm eval kit for Android, APQ 8084 and it has Bluetooth 4.1, which means it supports the LE mode. – MMJ Apr 20 '16 at 20:19
  • Did you check out the App @Markus Kauppinen suggested? – Jonas Köritz Apr 20 '16 at 20:34
  • I checked it, but every time when click on the the Beacon Transmitter it crashes. Also, through my code I am seeing a ToastMessage of "Multiple advertisement not supported", so does it means that I am not able to transmit the beacon? – MMJ Apr 20 '16 at 20:38
  • Could there be any other App on the device that is advertising, occupying the Hardware? Maybe there is some Kind of Demo Service running. – Jonas Köritz Apr 20 '16 at 20:43
  • I ran the Locate app and go to the Beacon Transmitter. But the Apply button is always disable. – MMJ Apr 20 '16 at 20:53
  • Are there any Apps supplied by the manufacturer running in the background? – Jonas Köritz Apr 20 '16 at 20:59
  • I don't think so. For the checking purpose I also downloaded the Locate app on one of the galaxy tablet which has Android 5.0 and try to run the app. I turned the slider of the app to "ON" but still Apply button is disable, is this correct? and I also have other tablet which scans for the beacon but it's mot getting the MAC address of this tablet which I use to transmit the beacon – MMJ Apr 20 '16 at 21:06
  • The App seems to be pretty faulty, please double check manually (Android Apps Settings Screen) that nothing is occupying the Hardware. – Jonas Köritz Apr 20 '16 at 21:10
  • I checked there is no other app which is using Bluetooth or any hardware related to Bluetooth – MMJ Apr 20 '16 at 21:14
  • Found another possibility. – Jonas Köritz Apr 20 '16 at 21:17
  • Sorry, I didn't get you. – MMJ Apr 20 '16 at 21:43
  • I did that also. Not included the device name. But no luck – MMJ Apr 20 '16 at 22:06
  • I got one success, my this code is running on ad Tablet and advertise the packet. But the think is, I need to run it on Snapdragon Eval kit and it's not working there. Always gives me error code = 2, ADVERTISE_FAILED_TOO_MANY_ADVERTISERS. – MMJ Apr 21 '16 at 20:19
1

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