If your smartphone runs out of battery and shuts down then it will reboot when you connect it to its charger. You can also find yourself in the same situation if your device reboots due to an internal error which caused the software to become unstable. However when a device is encrypted these reboots can have an unfortunate side effect, the device’s storage remains encrypted until the user enters their credentials. This means that apps that schedule alarms, or apps that provide important and timely notifications, can’t run.
This is a problem that Android 7.0 aims to solve, with the introduction of Direct Boot. In this article, we’re going to look at what this new Direct Boot feature is, and how to update your own Android apps so your users can reap the benefits of this new feature.
What is Direct Boot?
Direct Boot is, essentially, the new name for that strange no-man’s land where the device has finished booting but isn’t fully initialized yet. With this new name, comes new functionality, and developers can now create apps that provide some limited functionality during this period.
Once a device has finished rebooting, the data stored on that device remains encrypted until the user enters their credentials, such as their password, PIN or pattern. If you don’t enter your credentials then the device and all of its data remains encrypted.
Apps cannot function normally until the device is decrypted, so at this point the device cannot perform essential tasks such as receiving incoming calls, emails or messages. It also means apps can’t deliver notifications, or act on scheduled alarms – in fact, the only thing an encrypted device can really do is burn through its remaining battery life.
And remember that in some of these reboot scenarios the device has rebooted unexpectedly, so unless you just-so-happen to glance over at your smartphone or tablet and catch it in the act, then you’ll have no idea that a reboot has even happened.
Let’s take this one step further: imagine you’re waiting on an important notification, such as an SMS message about where to meet your friends for lunch, or an email containing the details about the telephone interview that’s supposed to take place at some point this afternoon. Unbeknown to you, your phone automatically rebooted an hour ago, so you never received the SMS from your friends asking where are you, we’ve been waiting for ages! Even worse, you missed the email with details about the phone interview that was supposed to happen half an hour ago.
Okay, so this is a bit melodramatic, but this worst-case-scenario stuff isn’t impossible – or at least, it wasn’t impossible in the pre-Nougat world.
With Android 7.0’s new Direct Boot mode, missing out on job interviews or lunch dates should be a thing of the past. This new, restricted mode allows applications to perform limited tasks and access specific sections of data, even when the device is encrypted.
This is particularly exciting if your app performs tasks that the user can’t afford to miss out on due to an unexpected reboot, for example if you’re developing an alarm app, or an app that provides crucial services to the Android system or other applications. Direct Boot can also be useful for accessibility apps, as it allows users to access these services as soon as their device boots, without them having to unlock their device first.
Enabling Direct Boot in your app
If your app includes features that need to run in Direct Boot, then the first step is working out which components are required to deliver this functionality, as these are the components that you’ll need to make Direct Boot aware.
Open your project’s Manifest and then add the directBootAware XML attribute to each component that needs to run in this mode, whether that’s a specific Activity, a service, receiver, or something else, for example:
<activity android:name=".MainActivity" android:directBootAware="true">
Once a device has finished booting but is still in its locked state, the system broadcasts a LOCKED_BOOT_COMPLETED intent. You’ll also need to tell your Direct Boot component(s) to listen out for this crucial message:
<activity android:name=".MainActivity" android:directBootAware="true"> <intent-filter> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> </intent-filter> </activity>
Finally, in order to receive the LOCKED_BOOT_COMPLETED broadcast successfully, your app needs to request the RECEIVE_BOOT_COMPLETED permission:
Accessing Data in Direct Boot
At this point, the user still hasn’t entered their credentials, so for security reasons access to data is restricted. Since your app won’t be able to access Android’s credential protected file system in Direct Boot mode, Android 7.0 introduces a new device encrypted storage area. This area uses Android Nougat’s file-based encryption to grant apps access to specific data – striking that tricky balance between security and convenience.
Let’s take a closer look at Nougat’s dual storage areas:
- Credential encrypted storage. The is Android’s default storage that’s only available once the user has unlocked their device. When an app is running in Direct Boot mode, it cannot access this encrypted part of the file system, however once the user has unlocked their device, the components that were previously running in Direct Boot can access this storage area as normal, just like any other application.
- Device encrypted storage. This is Android 7.0’s new storage area that’s accessible at all times, including during Direct Boot. Note that device encrypted storage remains accessible after the user has unlocked their device – just in case your app needs to access the data it’s stored here at a later date.
The availability of these different storage areas will impact on what your app can and can’t do, for example if you’re developing a messaging app, that app might be able to receive messages in Direct Boot mode, but if the contacts data is stored in the credential-encrypted area, then your app may not be able to reply to messages in Direct Boot.
The bottom line? Your app is going to have to use device encrypted storage for all its data needs while it’s in Direct Boot mode. To access this storage area, you’ll need to create and use a secondary Context instance, by calling Context.createDeviceProtectedStorageContext, for example:
Context deviceProtected = context.createDeviceProtectedStorageContext();
As soon as the user unlocks their device, your app will have access to credential encrypted storage, and will be able to perform more actions as a result.
Since the user unlocking their device is such an important event, you’ll want to ensure that your app is notified when this happens. The good news is that the system already sends out an ACTION_USER_UNLOCKED message whenever the user unlocks their device, so you just need to create a BroadcastReceiver in your project, to listen out for this message.
Finally, you can check whether the user has unlocked their device at any time, by calling isUserUnlocked(context).
Direct Boot Best Practices
What would a new feature be without some best practices? Here’s a few pointers on how to make the best use of Direct Boot in your own applications:
- Consider whether your need to use Direct Boot at all. Just because Direct Boot exists, doesn’t automatically mean you have to use it. This mode was designed specifically for apps that perform critical actions or issue notifications that the user can’t afford to miss. If this doesn’t sound like your app, then chances are you don’t need to make your app Direct Boot aware at all. And whatever you do, don’t use Direct Boot as a way of getting your app some extra attention by bombarding the user with less-than-urgent notifications as soon as their device finishes booting. In the long run your users are only going to get annoyed if it feels like your app is needlessly pouncing on them the second they turn their device on.
- Limit the amount of data you place in device encrypted storage. Since the data stored in Nougat’s new storage location isn’t protected by user credentials, you should try and save as little data there as possible. In the interests of security, aim to store the minimum amount of data your app requires in order to function when it’s in Direct Boot mode. In particular, you should never store sensitive information, such as passwords or authorisation tokens, in device encrypted storage. This kind of sensitive information always belongs in credential protected storage.
- Consider migrating existing preferences and data. If you do update your app to be Direct Boot aware, consider whether you have any previously-saved Shared Preferences or existing data that needs to be migrated to device encrypted storage. To migrate existing shared preferences files to a new location, you can use moveSharedPreferencesFrom, or use moveDatabaseFrom to migrate a database file.
- If your app has to fail, then make sure it fails gracefully. When your app is running in Direct Boot mode, it’ll only have access to other components that are also marked as Direct Boot aware. If your application depends on other apps or services, then you should design your app so that it fails gracefully if those particular components are unavailable during Direct Boot mode.
So what do you think of Direct Boot. Is it a feature that you will be adding to your app? Does you app need it? Please let me know in the comments below.