82
votes

I'm trying to make an application which uses the new Bluetooth Low Energy API of Android. For this, I started with the BLE sample coming with API level 18.

As I read that Android can not act as a Peripheral, I put the Android phone in central mode, scanning for BLE devices around it. For this purpose, I made some testing with a Nordic Platform simulating a Heart Sensor. Everything works in a perfect way!

After this, I try to pick an iPhone (iOS 7 beta 4) and put it in a Peripheral way and simulating a Heart Rate sensor as the previous testing. The Android app is able to see the device and connect to it. But after the connection is active, the 2 devices disconnect from each other in 3-4 seconds. In addition to that, when I call discoverServices() on Android side, no callback is triggered! In some cases the Android device receives the "Connected" event even if iOS Bluetooth chip is Off. This is very strange. To prove that, I put the Nordic Board in Central mode and I was correctly able to connect to the iOS device with no problems.

What could it be? There are some limitations on Android or iOS that don't permit to connect from an Android to an iOS or viceversa?

Thanks.

EDIT: After some hard testing, I raised an issue on the AOSP page. It can be checked here

5
According to the documentation, Android DOES support running as a server (for example a Heart Rate monitor) though the values one generates will be fake. On the other hand, there are no examples, the documentation tells you to do wrong things, and there is no way to start advertisements (though the documentation says you can).Brian Reinhold
You're right. I've already found the doc bug about BluetoothGattServer. You can not have the GattServer instance with getProfileProxy method (as doc says), but you can from the BluetoothManager.openGattServer(). It's already been reported to google as an issue. Anyway, yes Android can act as a GattServer but it can not advertise. If you might be interested, I've already tested and after the conenction of the 2 devices, the remote can see the Gatt servers exposed from Android. Check first answer's comments to see my issue report about this question.edoardotognoni
Yea, I stumbled on that too and added my two cents to that issue. Now I have a Thermometer server that can't advertise so I can't use it.Brian Reinhold
I opened a thread nearly identical to yours: stackoverflow.com/questions/18410081/… I will watch this thread for any solutions you find.afrederick
Read the Android issue I posted in the EDIT section of the question. It's clearly explain why this process is failing. it's an Android fault we think. Mainly is sending a not permitted message over a fixed BLE channel. I think the only thing we can do is to wait for a new Android release :(edoardotognoni

5 Answers

7
votes

Adding a summary for reference:

What could it be? There are some limitations on Android or iOS that don't permit to connect from an Android to an iOS or viceversa?

When connecting to a GATT server that is advertised as dualmode (BLE and BR/EDR) device by calling connectGatt(...), the TRANSPORT_AUTO flag that is internally added makes Android to default to the BR/EDR mode (link).

Following workarounds are possible:

  1. Peripheral side: Stop advertising BR/EDR capabilities by adjusting the appropriate flags (link)
  2. Central side: Set the transport parameter explicitely to TRANSPORT_LE by calling the hidden version of connectGatt() using reflection

Example:

public void connectToGatt(BluetoothDevice device) {    
   ...    
   Method m = device.getClass().getDeclaredMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class);    
   int transport = device.getClass().getDeclaredField("TRANSPORT_LE").getInt(null);     // LE = 2, BREDR = 1, AUTO = 0    
   BluetoothGatt mGatt = (BluetoothGatt) m.invoke(device, this, false, gattCallback, transport);    
   ... 
}

Edit 4/2016

As Arbel Israeli pointed out in the comment, Google introduced an overloaded version of connectGatt(...) which allows to specify the transport in Android M.

2
votes

I've written a simple working example, well relatively simple, and included it open-source on Github: https://github.com/GitGarage. So far it has only been tested with an Android Nexus 9 and an iPhone 5s, but I presume it would also work with a Nexus 6 and various iPhone types. So far it is set up explicitly to communicate between one Android and one iPhone, but I presume it is tweakable to do much more.

2
votes

Maybe a bit delayed, but perhaps your pain can be relieved slightly ;)

We have been experimenting a lot with cross platform BLE connections (iOS<-> Android) and learned that there are still many incompatibilities and connection issues. Aside to the instability of Android you should also consider that still, as of today, not that many Android devices actually support the BLE Peripheral mode.

Therefore, if your use case is feature driven and you only need basic data exchange I would suggest to look at Frameworks and Libraries that can achieve cross platform communication for you, without you needing to build it up from scratch.

For example: http://p2pkit.io or google nearby

Disclaimer: I work for Uepaa, developing p2pkit.io for Android and iOS.

1
votes

You can now pass in TRANSPORT_LE via BluetoothDevice.connectGatt as of API 23.

Please see Android Documentation references below:

0
votes

iOS Devices always be a peripheral or central but Android devices cant be rarely.In this case your iOS device must be a peripheral and android must be a central.We can think peripheral is a server and central is a client.This is simple.