This is the third in a series of Bluetooth™ beacon related posts by me (@jcmrim), and my colleague Martin Woolley (@mdwrim). You can find the two previous posts here and here if you haven’t already read them and the subject of beacons is new to you.
In this article, I want to move the story on and talk about how a beacon-aware BlackBerry 10 application ought to behave in the real world. The original WheresMyBeacon application, which was the subject of the two earlier posts in this series, demonstrated the mechanics of how beacons worked but suffered from one key drawback, namely: the application had to be running to detect beacons. Whilst this is useful whilst exploring the technology it’s not very useful in the real-world.
This post shows how to develop a beacon-aware application that is able to detect beacons even when the GUI part of the application is not running using features that have been introduced in BlackBerry 10 version 10.3 GOLD.
Let’s just remind ourselves of the use-case that we’re supporting and build on the example of the fictional supermarket chain, “Exco”, which I introduced in the first post in this series. Exco have deployed beacons in all their retail outlets and designed a beacon infrastructure to allow them to deliver special deals to customers as they walk around any of their stores.
Key to this use-case is that customers would have installed the “Exco Loyalty App” on their smart-phone; without this application installed the handset is unable to recognise a beacon as an “Exco” beacon rather than their rival’s beacons. This application will notify the user of any special deals as they wander around a store and also, potentially, provide statistics to Exco as to customer behaviour in the store.
Now, it’s not acceptable that a user would manually have to open the “Exco Loyalty App” every time they entered a store. What is required is that the application should be capable of recognising Exco’s beacons even if the application itself is not running.
Let’s see how this can be achieved on BlackBerry 10.
The key BlackBerry 10 technology for supporting this use-case is called “Headless Application” support. There are many explanations of how Headless Applications are structured and I’m not going to describe in detail how Headless applications work. I’ve provided links to some of these at the foot of this article. If you’re not familiar with the Headless Application architecture then this is a good time to go and learn about it.
At a very high level here’s what you need to know. A BlackBerry 10 Headless Application consists of two components:
- A GUI component which is identical to an ordinary BlackBerry 10 application capable of interacting directly with the user, and;
- A background service component which does not interact with the user and which has a more restricted application lifecycle when compared to the GUI component.
These two components are packaged into a single application BAR file and share the same sandbox so they can share data on the file system. The background service can be started when the device is started so it’s always running in the background but with certain constraints in terms of how much CPU cycles it can consume.
So, the key task to converting the WheresMyBeacon application into a Headless one is to move all of the Bluetooth™ interactions into the Headless component and utilise capabilities, such as the BlackBerry 10 Hub, to notify a user when a beacon detection event occurred in the Headless component.
The BlackBerry 10 Application: WakeMeByBeacon
This Headless application was built using the standard Headless Application template in Momentics which creates two interrelated projects which have been called “WakeMeByBeacon” and “WakeMeByBeaconService”. Let’s have a look, first, at the bar-descriptor.xml file in the GUI component project.
You can see that there are two permissions specified: one identifies the permission needed to allow this application to run a headless component, and the other identifies the permission needed to allow the application to add notifications to the BlackBerry 10 Hub.
Secondly notice that the invocation action “bb.action.system.STARTED” has been registered for the headless component. This ensures that the headless component will be started at device start time.
Initialisation of Bluetooth™ is identical to the “WheresMyBeacon” except that this now takes place in the headless service (in Service.cpp) rather than in the GUI.
The headless service also registers for a couple of SIGNAL()s: one from InvokeManager() so that it can handle Invocation Requests, and one from Application() which tracks the service’s process state.
So, now the headless component will start at device start time, be registered for Bluetooth™ advertisement events, be able to react to invoke requests and be aware of its processing state.
Now, let’s look at the GUI. It’s a simple interface with two basic actions:
- Start or Stop the headless service component.
- Enable or Disable scanning for Bluetooth™ beacons.
The ability to start and stop the headless service is a useful feature rather than having to re-install the application or restart the device. Similarly the ability to Enable or Disable scanning in the headless service for Bluetooth™ beacons is useful.
The buttons reflect the state of scanning and the headless service and there is a log section which is used to log certain events in the service.
Now, if you’ve been paying close attention you may have a question! How is it that the GUI component can possibly know about the status of the headless service, whether it’s running or not, and whether or not the headless service is currently scanning for Bluetooth™ devices?
The GUI isn’t cheating or guessing the headless service state; it actually knows the status of the headless service. How is it doing this?
Let’s go back for a minute to the headless service again to see how this is done. If you look at the code for the headless service you’ll find a class called SocketListener(). An instance of this class is instantiated in the headless service and provides a QLocalSocket() server running in the headless service. When the GUI starts it tries to connect to this local socket() and establish a two-way communication channel between the headless service and the GUI.
SocketListener() also exposes some SIGNAL()s which can be used to determine when the GUI connects and disconnects and a SLOT() to allow the headless service to send data over the socket() to the GUI.
So, what does the headless service send over this connection? If you look in the application’s console log when you run it from Momentics you’ll see examples like this:
Periodically, it sends a JSON encoded message indicating the current status of the headless service to the GUI so that it can use this information to reflect the state of the headless service. The GUI is also aware of the state of the socket() to the headless service so it knows whether it’s running or not.
So, it’s not magic; just a simple socket() between the two components. This is a useful pattern to re-use. In fact I re-used it from this more generic headless sample application that we have written and blogged about here.
Similarly, sending instructions to the headless service through invoke requests is quite simple as well. Here’s the action that’s hooked up to the button that instructs the headless service to start scanning for beacon advertisements:
In fact here’s the code fragment in the headless service that handles starting and stopping of scanning for Bluetooth™ beacon advertisements:
Now, this is the point at which we begin to differ from the previous application, “WheresMyBeacon”. The functions bt_le_enable_scan_invoke() and bt_le_disable_scan_invoke() are new in BlackBerry 10 version 10.3 GOLD. They enable you to request that an application be invoked when a Bluetooth™ event occurs, which means that the headless component of your application can stop running and be restarted when a suitable Bluetooth™ event occurs. This is exactly what we do in this application.
Lets’ have a look at how the headless service deals with such an event.
The “bb.action.bluetooth.SCANRESULT” invocation action is the one we have to handle and then decode the advertisement data in the request payload using the utility function bt_le_invoke_decode_scan_result().
So, if we now stand back a bit we can see that the headless service will receive Bluetooth™ advertisement events from two sources:
- If the headless service is running they will be received through the standard call-back scheme that we demonstrated in the WheresMyBeacon application.
- If the headless service is not running when a Bluetooth™ advertising event occurs it will be delivered through the Invoke mechanism.
These two sources of beacon advertisements will converge when the headless service needs to decide what to do with these events – does it tell the GUI? And, if it does, how does it tell the GUI?
First of all, all new beacons that are detected are added to a data model with a time-stamp to show when they were last seen. If a beacon has gone out of range it will be removed from the data model after a certain period of time – I compromised with a time of 5 minutes in this application, feel free to experiment.
This means that the headless service can generate events for the GUI when a new beacon is detected and also when a beacon has gone out of range. Let’s take a look at the case of a new beacon having been detected – the process is identical for one moving out of range. Here’s the code that notifies the GUI:
If the headless service knows that the GUI is running (remember the socket() connected to the GUI?) it sends a message over the socket() for the GUI to deal with.
If the GUI is not running then the headless service constructs a Notification() to be placed in the BlackBerry 10 Hub that will launch the GUI and with a payload that contains the same data that would have been sent across the socket(). Here’s an example of the message that gets sent to the GUI from the headless service when a new beacon is detected, and the same data as it’s rendered in the GUI.
Apart from some bits and pieces of housekeeping that’s pretty much it for the headless component. If the GUI is not running when the headless service detects a beacon event the notification it sends to the hub appears something like this.
It can be used to launch the GUI component application which then can take action on the beacon that’s been discovered. Here’s how that’s done:
I’ve released V1.0.0 of the WakeMeByBeacon application containing the features described in this article and so you can download, review and reuse the full source code – you can find the URL in the “Resources” section below. I hope this has been interesting and will help set you on your way developing real-world beacon applications for Blackberry 10.
Please feel free to contact myself @jcmrim via Twitter if you need any help on anything Bluetooth Low Energy related.