BlackBerry OS 10.2.1 introduced headless applications, which allowed an application process to run in the background without any user interface. There are multiple ways headless applications can be started. A popular choice is to start the headless part when the device starts using the bb.action.system.STARTED trigger. That trigger also starts the application when it is first installed. This is useful for applications that always need to be running in the background, but can lead to inefficiencies and a poor user experience. If you want to jump straight to a sample application, have a look at the DeferredAutoStart sample on Github. Otherwise let’s take a look at a couple of scenarios where this isn’t ideal.
Example Scenarios We Can Improve
Headless Applications that Require Credentials from the User
The first scenario applies to applications that require credentials that the user has to supply using the GUI part of the application. If the headless application isn’t able to do anything useful without those credentials, all it can do is run, check if the credentials are present and exit if they are not. Starting it every time the device boots means wasted battery and CPU resources.
Corporate Applications Pushed from a BlackBerry Enterprise Server
Applications that use the system started trigger are also started when they are installed. The prompt for the user to grant the required permissions to an application is shown when the application runs for the first time. This means that the permission prompt is shown to the user when the application is installed. That can confuse a user when such an application is pushed out from a BlackBerry Enterprise Server because the user might be prompted without any context on what triggered the prompt.
Note that the prompts described above are suppressed for devices in EMM Regulated mode.
The Solution
The solution to both of these scenarios is to defer the auto run on startup feature until the user has launched the GUI part of the application. When they launch the GUI they can approve the permission prompt they expect to see. They can also enter credentials required by the headless application. Once one, or both – depending on the application’s requirements – of these requirements are met the application can configure itself so that the headless part is auto run on startup.
How We Solve It
The first step is to remove the bb.action.system.STARTED filter from the bar-descriptor.xml of the application like this:
<invoke-target id="com.example.DeferredAutoStartService">
<invoke-target-type>application.headless</invoke-target-type>
<invoke-target-name>DeferredAutoStart Service</invoke-target-name>
<entry-point-id>DeferredAutoStartService</entry-point-id>
<!-- Removed the filter to prevent auto run on startup.
This is added back at runtime once we want to enable auto run on startup.
<filter>
<action>bb.action.system.STARTED</action>
<mime-type>application/vnd.blackberry.system.event.STARTED</mime-type>
<property var="uris" value="data://local" />
</filter>
-->
</invoke-target>
The invoke-target id is still defined and we will modify that later. Removing the filter prevents the bb.action.system.STARTED headless trigger from firing, preventing the headless application from running when installed and on startup.
The next step is to update the GUI application to add that filter back once all requirements (permissions accepted and/or credentials entered) for the headless application to function are met. To do that you create an InvokeTargetFilter that matches the one that was removed from your bar-descriptor.xml. An InvokeTargetFilter that matches the example bar-descriptor.xml code above would look like this:
InvokeTargetFilter autoStartFilter;
autoStartFilter.addAction("bb.action.system.STARTED");
autoStartFilter.addMimeType("application/vnd.blackberry.system.event.STARTED");
autoStartFilter.addUri("data://local");
Next you need to create an (take a deep breath here) InvokeUpdateTargetFiltersRequest that points to your invoke-target id.
InvokeUpdateTargetFiltersRequest updateRequest;
updateRequest.setTarget("com.example.DeferredAutoStartService");
QList<InvokeTargetFilter> filterList;
filterList.append(autoStartFilter);
updateRequest.setTargetFilters(filterList);
The final step is to use the InvokeManager to update the invoke target filter.
InvokeManager manager;
manager.updateTargetFilters(updateRequest);
Once that’s complete your application is now configured to auto run on startup. But the headless application won’t be started until the device is rebooted. To start it now, initiate an InvokeRequest.
InvokeRequest request;
request.setTarget("com.example.DeferredAutoStartService");
request.setAction("bb.action.system.STARTED");
manager.invoke(request);
The Sample
You can download a complete sample application that implements this technique from our Github site here: DeferredAutoStart