This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

BluetoothLeAdvertiser.stopAdvertising() causes device to disconnect

I develop an Android app which connects to a Nordic chip, and I cannot get the Android app to stop advertising once it connects to the central from the SDK multi link project.

I  was curious if any one from Nordic might be able to have some insight into why the Android phone cannot be prevented from advertising once it's connected to a central. 

I know that Nordic chips stop advertising once they connect, and in fact I thought this behavior was a specification in the BLE protocol. 

I would like to be able to format my code code with the old mark up language this web site used to sue like Stack Overflow does, and I have no idea why I can long use the old way for formalizing questions. Any explanation?

Here my BluetoothGattServerCallback where I handle two events `STATE_CONNECTED` and `STATE_DISCONNECTED`

private static class CallBack extends BluetoothGattServerCallback {

private Set<BluetoothDevice> mRegisteredDevices = new HashSet<>();
private MainActivity mMainActivity;

public CallBack(Set<BluetoothDevice> registeredDevices, MainActivity mainActivity){
mRegisteredDevices = registeredDevices;
mMainActivity = mainActivity;
}

@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {

Log.i(TAG,"onConnectionStateChange()");

switch (newState)
{
case STATE_CONNECTED :
handleStateConnected( device, status, newState);
break;
case STATE_DISCONNECTED :
handleStateDisconnected( device, status, newState);
break;
}
}

Here is handler `STATE_CONNECTED`

private void handleStateConnected(BluetoothDevice device, int status, int newState)
{
Log.i(TAG, "BluetoothDevice CONNECTED: " + device);
mRegisteredDevices.add(device);
stopAdvertising();
}

Here is handler `STATE_DISCONNECTED`

private void handleStateDisconnected(BluetoothDevice device, int status, int newState)
{
Log.i(TAG, "BluetoothDevice DISCONNECTED: " + device);
mRegisteredDevices.remove(device);
startAdvertising();
}

On `STATE_CONNECTED` I would like my phone to stop advertising, and on `STATE_DISCONNECTED` I would like the phone to resume advertising; the default behaviour appears to be that the device will continue advertising after it's connected, so I added `stopAdvertising()` and `startAdvertising()` in the event handlers `handleStateConnected()` and `handleStateDisconnected()` respectively.

The phone now goes into an infinite loop of connecting and reconnecting, the reason being that after calling `stopAdvertising()` the phone will disconnect, causing it to begin advertising again, after which it will connect.


The control flow of my app is to begin advertising, then set up GATT server:

private void initServer(){

Log.v(TAG,"initServer()");

mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);

BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();

// We can't continue without proper Bluetooth support
if (!checkBluetoothSupport(bluetoothAdapter)) {
finish();
}

// Register for system Bluetooth events (GATT server started in receiver)
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mBluetoothReceiver, filter);

if (!bluetoothAdapter.isEnabled()) {
Log.d(TAG, "Bluetooth is currently disabled...enabling");
bluetoothAdapter.enable();

} else {

startAdvertising();
startServer();
}
}

Here is `startAdvertising()`

public static void startAdvertising() {

Log.i(TAG,"startAdvertising()");

BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();

mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothLeAdvertiser == null) {
Log.w(TAG, "Failed to create mBleAdvertiser");
return;
}

Log.v(TAG,"created advertizer");

AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(true)
.setTimeout(0) // Limit advertising to a given amount of time A value of 0 will disable the time limit
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.build();

boolean isNameChanged = BluetoothAdapter.getDefaultAdapter().setName(DEVICE_NAME);
if(isNameChanged) Log.d(TAG,"Device name changed successfully.");

AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName(true)
.setIncludeTxPowerLevel(false)
.addServiceUuid(ParcelUuid.fromString(Services.GloService.UUID))
.build();

mBluetoothLeAdvertiser
.startAdvertising(settings, data, mAdvertiseCallback);
}

Here is `startServer()`

private void startServer() {

Log.i(TAG,"startServer()");

mBluetoothGattServer =
MyProfile.getOpenServer(this, mBluetoothManager,mRegisteredDevices,this);

if (mBluetoothGattServer == null) {
Log.w(TAG, "Unable to create GATT server");
return;
}
}


And here is `MyProfile.getOpenServer` which returns the server object initialized with the call back declared at the top of the post.

public static BluetoothGattServer getOpenServer(Context ctx,
BluetoothManager bluManager,
Set<BluetoothDevice> registeredDevices,
MainActivity mainActivity){
mBluetoothManager = bluManager;
CallBack callBack = new CallBack(registeredDevices, mainActivity);
BluetoothGattServer server = bluManager.openGattServer(ctx, callBack);
BluetoothGattService myService = getMyService();
server.addService(myService);
return server;
}

Parents
  • Hello,

    First of all, sorry for the devzone experience. Don't know what to say, I'm not responsible for this Disappointed

    Coming to your question: advertising is independent from the connections. On Android a phone may actually advertise with many different advertising sets, each with a different, changing from time to time address, etc. You control advertising separately from connections.

    Your approach to stop advertising when a remote device connects is correct. Remember to call `server.connect(device, autoConnect)` after a device connected. This is to let the system know that you're now going to use the connection, otherwise the system may close it if you don't do `device.connectGatt(...)` (which also tells the system, that you will actively use the connection in your app). When you are done, call `server.cancelConnection(device)`. If `server.connect(...)` won't help, call `connectGatt(..)` to get access to the device`s services.

    Stopping advertising should not disconnect anything, just like it doesn't disconnect in nRF Connect when you stop advertising. It may be either the automatic disconnection, which happens a second after last client disconnected (or when your device connected, but no client connected to it), or some mistake in your code.

    Hope that helped!

    BR, Aleksander

Reply
  • Hello,

    First of all, sorry for the devzone experience. Don't know what to say, I'm not responsible for this Disappointed

    Coming to your question: advertising is independent from the connections. On Android a phone may actually advertise with many different advertising sets, each with a different, changing from time to time address, etc. You control advertising separately from connections.

    Your approach to stop advertising when a remote device connects is correct. Remember to call `server.connect(device, autoConnect)` after a device connected. This is to let the system know that you're now going to use the connection, otherwise the system may close it if you don't do `device.connectGatt(...)` (which also tells the system, that you will actively use the connection in your app). When you are done, call `server.cancelConnection(device)`. If `server.connect(...)` won't help, call `connectGatt(..)` to get access to the device`s services.

    Stopping advertising should not disconnect anything, just like it doesn't disconnect in nRF Connect when you stop advertising. It may be either the automatic disconnection, which happens a second after last client disconnected (or when your device connected, but no client connected to it), or some mistake in your code.

    Hope that helped!

    BR, Aleksander

Children
Related