I am getting started with BLE development. I am trying to dig into the source code of a SensorTag Android app. But my small brain is having some difficulties in understanding the callbacks conceptually. According to the the callback explained by Wikipedia, it is a routine called within another routine upon some trigger. I would like to know how callbacks are applied in the context of BLE. For example, why reading and writing a characteristics on BLE server need callbacks? Thanks!
They need callbacks because all of BLE is asynchronous. So, you can request to read an attribute, some X other events can occur, and then the data you requested shows up. Unless you want your program to freeze until it gets the data you requested, you have to postpone processing the result with a callback.
Related
I am developing an android app with BLE API from android. My app needs to connect to a BLE device, and remain connected as long as it is in range and turned on. I need to read data from it, and write data to it.
I am trying to follow the MVP architecture pattern, not strictly since activities are the starting point. But anyway, I wanted to know where should I put the interaction with Bluetooth? I am searching for answers for the following questions. I have searched StackOverflow, but couldn't find what I was looking for.
Should it be in a service bounded to the UI just like in googlesample ble app ? But, I think that would break the whole mvp architecture.
Should it be a bounded service at all ? If no, what would be the best way to implement the service? In my mind, if it's not bounded to the view, and there is a callback from the background service to display something on the UI, there is a possibility of undefined behavior.
Who should initiate the Bluetooth interaction ? The application class or some activity ?
I am looking for mainly architectural guidance, and best way to go about developing this app.
Since you have the requirement that the Bluetooth connection should keep working in the background, you should have a Foreground Service somewhere running in your app process. This will make sure your app process will be kept alive, but requires an icon to be displayed in the phone/tablet's top bar.
Whether you actually put your BLE code in this service class or not doesn't matter for the functionality.
There are of course many ways to achieve good architecture but here is my approach.
My approach would be to have a singleton class that handles all your BLE scanning, connections and GATT interactions (from now on called Manager). Since some BLE operations needs an Android Context, a good way is to use the Application context as context. Either follow Static way to get 'Context' on Android? to be able to fetch that context at any time or subclass the Application class and from its onCreate call some initialization method in your Manager and pass the context. Now you can keep all BLE functionality completely separated from Android Service/Activity/Application stuff. I don't really see the point in using bounded services etc. as long as you keep everything in the same process.
To implement a scan functionality, you can have a method in your Manager that creates Scanner objects. Write the Scanner class as a wrapper to Android's BLE scanner and expose methods to start/stop scan. When you create a Scanner that method should also take an interface as argument used for callbacks (device reports and errors). This class can now be used in for example an Activity. Just make sure that the scanner gets stopped in the Activity's onStop method to avoid leakage of objects.
There are several reasons for having a wrapped custom Scanner object instead of using Android's BLE scan API directly in the Activity. First you can apply the appropriate filtering and processing of advertising packets so it handles your type of peripheral and can show high level parameters (decoded from advertising data) in your custom advertising report callback. The manager should also listen to broadcasts when Bluetooth gets started/stopped/restarted and keep track of all started Scanners so the Scanners are restarted seamlessly when Bluetooth restarts (if you want this functionality). You may also want to keep track of timestamps of all scan starts/stops so you can workaround the new restrictions in Nougat that limits it to 5 scans per 30 seconds.
Use a similar approach when you want to connect to your peripherals. You can for example let the Manager create Device objects which have methods to start/stop the connection and have a callback interface to report events. For each supported feature (for example read some remote value) you should expose a method which starts the requests and have a callback which is called when the result arrives. Then your Manager and Device class takes care of the GATT stuff (including enqueuing all your GATT requests so you only have one outstanding GATT operation at a time). Just make sure you can always abort or ignore the result when you don't want the result, for example if an Activity's onStop or onDestroy method is called.
Since you probably want to reconnect automatically in case the device gets disconnected, you should use the autoConnect flag and set it to true when establishing the connection, which assures this. Again, the Manager should keep track of all active Device objects and automatically recreate the BluetoothGatt object when Bluetooth is restarted.
To be able to display different kind of UI stuff, like for example automatically show a warning message in your Activity when Bluetooth is turned off and remove it when Bluetooth is turned on, you should be able to register Listeners to your Manager. Have a method in your Manager for registering/unregistering a listener (which is really just a Callback) object, keep track of all the listeners and when Bluetooth state change happens, call all listeners. Then in your Activity's onStart you register a listener and in onStop you unregister it. You can have a similar approach for your Device's BLE notifications, where applicable.
What's left is how you deal with different Threads. As you might know most BLE callbacks from Android's API happen on Binder threads, so you may not update the UI from them. If you otherwise in your app don't use anything other than the main thread, you can for example post all invocations of callbacks in the Manager to the main thread, or maybe move to the main thread directly when the callback from Android's BLE stack arrives (but then be aware of things like https://bugs.chromium.org/p/chromium/issues/detail?id=647673). Just make sure you never touch the same variables from different threads.
Also if you target API 23 or higher you need UI code to let the user give permission to Location to be able to start scan. I suggest you implement this in your UI code and not in the Manager, or implement some "wrapper" or helper method in the Manager to do this.
RxCentralBle provides a paradigm for use in an app. The library design clearly shows the structure of the library. In short, RxCentralBle provides reactive interfaces for the primary Bluetooth LE actions:
BluetoothDetector - detect phone Bluetooth State
Scanner - scan for peripherals
ConnectionManager - connect to a peripheral
PeripheralManager - queue operations to communicate with a peripheral
It's recommended to subscribe to these interfaces on a background thread and ensure resources and subscriptions live at the application scope i.e. member variables of your Application class. As long as your Application is running, all Bluetooth LE resources will remain alive and active.
Check out RxCentralBle's Wiki and sample app to learn more.
I'm trying to write BLE Android app. I found that sometimes when I call
BluetoothGatt.writeDescriptor() it returns false.
I have not found in documentation any note of limitation to this function. But ppl on stack overflow says that I need to wait for BluetoothGattCallback.onDescriptorWrite() before I try to write another descriptor.
Here is one reply saying that BLE is busy with writeDescriptor() and can not do other write.
Here is another thread saying that you can not call twice writeCharacteristic().
My questions are
is it really true?
is there really missing some internal android API buffer for serializing BLE requests and every developer has to do it on it's own?
Is it true for different functions? For example when I call writeDescriptor() I understand I can not call second time writeDescriptor() before I receive onDescriptorWrite(). But do I have to wait for onDescriptorWrite() when I want to call writeCharacteristic()?
Also if there is inter-function dependency then what else function have this limitation (namely: readCharacteristic(), readDescriptor(), requestMtu()...)?
And additionally is there interdependency between BluetoothGattServer and BluetoothGatt. So for example when I call BluetoothGattServer.notifyCharacteristicChanged() shall I wait forBluetoothGattServerCallback.onNotificationSent before I can call BluetoothGatt.writeDescriptor() or BluetoothGatt.writeCharacteristic()? (BTW praise for google documentation onNotificationSent() is by luck documented properly. Doc says:
When multiple notifications are to be sent, an application must wait
for this callback to be received before sending additional
notifications.
Lastly having all this questions - I feel that Android BLE API is under-documented. Or am I wrong and there is documented somewhere what are allowed methods calling sequences? If yes can you please point me to such documentation? If not is there some channel we can open issue with google and ask them to add to documentation something? I mean it may not be much text - some function like onNotificationSent() is arleady properly documented. They just need to copy this sentence to other functions.
The documentation lacks information. However you can read the source code to find out the rules, which (currently) are the following:
For each BluetoothGatt object, you can only have one outstanding request at a time, including requestMtu, readCharacteristic, writeCharacteristic, readDescriptor, writeDescriptor and executeReliableWrite. So if you issue a read request you need to wait for the read response before you issue a write request. While they implemented the code that returns false if there is an ongoing operation in BluetoothGatt.java, they forgot to do this for requestMtu, so if you have multiple requests at a time where requestMtu is one of them, you will get random errors sooner or later (in the latest versions at the time of this post).
So yes, every developer has to manually serialize the requests. Note that the Bluetooth stack actually has a queue of requests, but it is limited to only one request per client (i.e. BluetoothGatt object). So if two apps on the same phone talk to the same device simultaneously you will never get "busy" errors. The only exception is if you use Write Without Response for which the current data flow implementation is quite buggy (see https://issuetracker.google.com/issues/37121017 which Google seems to have ignored).
You can send notifications at the same time as you write a characteristic, since the server and client roles are separated.
Regarding updating the documentation, you can always try to file an issue at https://issuetracker.google.com (but I get the feeling nobody reads that), or, since Android is open source, send a pull request to https://android-review.googlesource.com/ which updates the Javadoc from which the documentation is generated.
Inside an android Service I connect to the Microsoft Band 2 and read data from all the sensors.(I want to receive data even if the screen is locked).
However, after a while I stop receiving any data (no more callback are called). Also, there is not event coming on the connection callback - where I should expect connection states changes to be signaled.
I should also mention that I am registering for all possible sensor events.
My code is similar to the one in the documentation examples, but I can provide snippets if useful (the documentation contains samples for connecting inside activities but it should work the same).
Has anybody encountered this issue or a similar behavior?
You should probably enable WakeLock. I had my MBand2 transfer realtime data (via a Service) throughout the day/night with no data loss or connection issues.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
I need a good way to get data from a thread to another activity.
I have one gaol: how to develop an Android app that plots bluetooth data forever in real-time.
I inherited a background thread which updates the screen with new data it receives over a bluetooth connection. When I launch my Plot activity I can see a bluetooth background thread continue to write bluetooth data to Logcat forever and ever. So I know for a fact the bluetooth background thread is still running when I launch my Plot activity.
I have succeeded as follows: since this bluetooth background thread seems to run fovever, I decided to use its update() method to call my static Plot.plotData() method to plot the bluetooth data. And this works. It will will run endlessly with out a problem - receiving bluetooth data and plotting it via periodic calls from the bluetooth background method update() to my static Plot.plotData() method.
The latest feedback I have received "It sounds like you're looking for an in-memory way to share data, and that's simply not the way the Android activity model works." In-memory???But this is rediculous. An Android phone has a limited amount of RAM for running Activities (one at a time), threads, handlers, Services, AnycTasks, etc. And an SD card for persisting data.
One critical person basically said: "In order to share data from a bluetooth background thread to my Plot activity (Plot.plotData()) that I must use the SD card." ??? This just sounds nuts, because I have it working using my static method Plot.plotData().
Frankly I don't see anything wrong with my solution primarily because those who criticize it do not follow up with a definitive alternative.
If you find my solution deficient please speak up and provide a definitive solution." Unless I use a new thread, handler, Service, AsyncTask, etc, everyone assumes my solution is not a good one. Why? Supposing the criticism is valid, PRECIOUSLY What should I do instead of what I am currently doing?
One more time: if you have background task running you should create Service. That's how Android expects applications to behave. Otherwise your thread may be terminated at any time (assuming none of the Activities are active).
From documentation:
A service is a component that runs in the background to perform long-running operations or to perform work for remote processes
This precisely describes your case.
Also carefully read Processes and Threads, specifically Process Lifecycle section.
Basically your application falls into Background Process or Empty Process group most of the time. That's why you need Service started in your application.
I've asked various questions related to one goal: how to develop an Android app that plots bluetooth data forever in real-time. I now have a new question from a different angle. To be fair to new readers I will cover some of the same background, but basically the question is in the title.
I found some open source code Bluetooth that apparently creates a background thread which updates the screen with new data it receives over a bluetooth connection. Then when I launch my Plot activity I can see the bluetooth background thread continue to write bluetooth data to Logcat. So I know for a fact the bluetooth background thread is still running when I launch my Plot activity.
Again, my goal is to plot the bluetooth data that is continually provided by the bluetooth background thread. I have succeeded as follows: since this bluetooth background thread seems to run continually and just won't die, I decided to use its update() method to call my static Plot.plotData() method to plot the data. And this works. It will will run endlessly with out a problem - receiving bluetooth data and plotting it via periodic calls from the bluetooth background method update() to my static Plot.plotData() method.
Although I have received some negative feedback regarding my solution, I have found sharing data across Activities where Edward Falk says sharing static data is OK. Here Edward Falk asks: "Are the activities all in the same application? [yes] Same task? [not sure] It seems to me that you could just store the data in a static variable accessible by all of the activities." Well I tried static data and it worked, but I switched to a static method Plot.plotData() which seems to work better for me.
The latest feedback I have received from one person #emmby that says my static Plot.plotData() goes against the following: "It sounds like you're looking for an in-memory way to share data, and that's simply not the way the Android activity model works." But what am I else to do? I thought that an Android phone has a limited amount of RAM for running Activities (one at a time), threads, handlers, Services, AnycTasks, etc. And an SD card for persisting data.
#emmby seem to say that in order to share data from a bluetooth background thread to my Plot activity (Plot.plotData()) that I must use the SD card. This doesn't sound right. After all I have it working using my static method Plot.plotData().
Frankly I don't see anything wrong with my solution primary because those who criticize it do not follow up with a definitive alternative.
If you find my solution deficient please speak up and provide a definitive solution. Otherwise I will take Edward Faulk's solution as the best one.
If I understood you correctly, your thread is running at the background even when none of the activities display data obtained by that thread. For example, let's consider scenario:
You started your bluetooth thread.
Your activity A started and now read data from that thread.
Your activity A is backgrounded (for example user navigates away via "Recent" tasks).
Your activity is idle but bluetooth thread still consumes cpu and power.
If you don't use your bluetooth data you shouldn't run the thread. You can introduce some kind of counter that tracks number of clients that use your thread's data. And if counter drops to zero - thread stops/pauses.
But Android has built-in solution for such cases. You can introduce service which will be bound by different activities and will have DataSource interface with getNextData function. See Bounded Services for more details.
When there are no clients bound to service Android will stop the service. This way you completely decouple Activities from thread. Your bluetooth thread is now managed by Service. And Service is now managed by Android.
In your case Local Service should be enough to share data between your thread and activities.