Brian Long Consultancy & Training Services
Ltd.
November 2018
Accompanying source files are available to download.
Delphi's Android support has included support for application permissions since its introduction in XE5. This was necessary for Android apps to operate successfully in the Android world. If, for example, you need to have your app know the user's location then you must select the relevant permissions in the project options dialog:
Specified permissions are emitted into the application manifest file and can be examined as required by the system. This snippet of your project's AndroidManifest.template.xml is the permissions placeholder:
During the build process it gets expanded to this in the generated AndroidManifest.xml in order to be packaged up into the generated Android .apk file:
Permissions requested this way are established/verified during application installation for applications built with Delphi 10.2.3 and earlier. When an app is downloaded from the Google Play store, for example, the user is told about the permissions that are required and they can accept or decline the installation based on this.
This setup has served Delphi applications well enough until Google changed the rules of engagement.
From Android 6.0 (Android Mashmallow) or, more specifically, for apps running on Android 6.0 or later and targeting API level 23 or later, there is a new permissions model: Runtime Permissions. The idea is that for a subset of permissions, deemed to be dangerous permissions, the application must take certain steps. Dangerous permissions include those which put the user's data or applications at risk, and include reading and writing contacts, using the camera or microphone or accessing the user's location.
If you need to do something in an app that requires a dangerous permission to be granted then it must be specified in the manifest file as before, but in addition the app must request the permission at runtime: the user must be prompted to grant the permission, which of course they can permit or deny. The application must be sure to behave appropriately in the case that the user denies the requested permission. In addition to this the dangerous permissions can be granted or denied at the user's behest in the application settings on the device, meaning that a previously granted permission can very well be denied the next time it is required.
For some while this was an optional scheme - if you did not target Android 6.0 or higher then it did not affect you. Delphi 10.2.3 and earlier targets API Level 14 (Android 4.0) and so were not affected.
However at the end of 2017 Google announced that the rules were changing in 2018. In order to submit apps to the Google Play store they must now target API Level 26 (Android 8.0) or higher. Complying with this new requirement means that Delphi apps will now need to work with the runtime permissions model, something they could not do until Delphi 10.3 Rio.
All of this new required behaviour necessitates new (or slightly reworked) code in your application for the affected (dangerous) permission(s) and some asssociated new code in the Android parts of the FireMonkey framework and the RTL (runtime library). RAD Studio 10.3 Rio adds in the required underpinning to allow all this to be achieved.
This article looks at what is required and guides you through the process of implementing runtime permissions in your application using Delphi 10. Rio3.
At the lowest level Android runtime permissions require a couple of things:
Activity
underlying your Delphi application) it needs to be handled appropriately.Assuming the permission was granted and not denied then the program can proceed and do what it wants to do. Of course if the program tries to proceed without the permisison being granted then the Android OS will throw up some sort of security exception and the code will not be able to proceed. For applications you developed in earlier versions of Delphi you may need to alter the logic flow to take this into account.
As mentioned, handling the Activity
callback is a requirement at the lowest level. In Delphi 10.3 Rio (and later) the code that represents the Activity
does indeed respond to the required callback, Activity.onRequestPermissionsResult
. This broadcasts the results around to any interested parties using the RTL message subscription system (via a TPermissionsRequestResultMessage
message) similar to how it surfaces the onActivityResult
and the onNewIntent
callbacks (via
TMessageResultNotification
and
TMessageReceivedNotification
messages respectively).
When you want to address the requirement for runtime permissions in your Delphi 10.3 application you can take one of two approaches. You can:
Generally you are strongly advised to work with the new permissions framework and certainly all the product demos that require runtime permissions to work use the permissions framework, which is available through the System.Permissions unit in the RTL. However just for completeness I'll show both approaches in this article. I'll start with the advised approach using the new framework and then at the end I'll cover the lower level approach, which you can skip if of no interest to you.
To see how we can implement the request for and the response from runtime permissions let's assume we have an application that wants to know the user's location so that a TMapView
control can display a control to centre the map on the user's location and render a circle approximating the user's location on the map. In Android terms this means we need to have the ACCESS_COARSE_LOCATION
and/or ACCESS_FINE_LOCATION
permissions granted.
Just before getting stuck into the details I'll just point out that an application using a map control (as this sample app does) needs a couple of things to be enabled:
In the Version Info section of the project options the apiKey
entry must be set to your Google Maps API Key. You get an API key by following the instructions in the documentation. In this screenshot I've clearly indicated where you enter your API key. If you don't do this then at best your map won't display, but at worst your app will fail to install or will crash when trying to display the map.
In the Entitlement list section of the project options the Maps Service entry needs to be enabled. This ensures that appropriate entries are inserted into the <application>
section of the application manifest file thus (obviously your entered API key will be used instead of the prompting text in the first element):
Okay, now we've got those preliminary steps for the sample app covered, let's get back to implementing runtime permissions.
Without these location permissions granted (actually both are not required, just one of them should be enough) the map is unable to ascertain the user's location and so the code logic wants to ensure the relevant properties (TMapLayerOption.UserLocation
in the LayerOptions
set property and TMapControlOption.MyLocation
in the ControlOptions
set property) are not added to the map if the user location permission(s) are not granted.
Since these permissions still need to be added to the Android manifest file they must still be set in the project options:
The code to request the permission from the user is shown below. The map setup code is tucked away in a helper routine called SetupMap
, which if passed True
adds in the user location options, or if passed False
it ensures the user location options are not added. None of that is visible in this code snippet, however, because we have to wait for the user's response to the permission request.
What we can see is that if the permissions are already granted or we are running on an OS older than Android 6.0 (SDK version 23, aka API level 23) then the SetupMap
routine is simply executed with a parameter value of True
. Otherwise we run the code to request the permissions and do nothing further until we know what the user has decided.
With this the user will see something like:
When the user does respond to the permission request the information about that will be passed into the routine LocationPermissionRequestResult
(that takes advantage of the new inline variables and type inference also added in 10.3 Rio). Here you can see that the aforementioned SetupMap
routine is now called with a Boolean
value appropriate to the permission grant status.
As you can see the granted status of the permissions is examined and this then controls the parameter passed into the SetupMap
call.
Note that the PermissionsService.RequestPermissions
method can alternatively take an anonymous procedure as a parameter. This is demonstrated by some of the demo apps, including the one in Object Pascal/Mobile Snippets/Location, which has this code within it:
If the application goes down an execution path that requests permissions from the user who proceeds to deny them that is fine. We can act accordingly.
If the application later goes down the same execution path and again requests permissions from the user (and potentially again and again) without some form of additional explanation then this could get quite annoying or perhaps frustrating for the user.
Given this potential Google recommends taking this into account when asking second and subsequent times. This can be done quite straightforwardly with a small change to the call that requests the permissions in the first place. There is an optional procedural parameter that can be passed as shown below. If it makes sense to do so then that routine will be called and it is expected to display an additional explanatory message to the user explaining why we want the permission. In other words the app can offer up a rationale as to why the permission is important and being asked for again.
Note that this explanatory message must be asynchronous. Also notice that the called routine, DisplayRationale
in this case, is passed a procedural parameter that must be called after the user has dismissed the explanation from screen. This post-rationale procedure proceeds to request the permission again. Given that the user has seen explanatory infromation about the permission request we may potentially get a more favourable response from them.
The user sees the rationale message ahead of any subsequent permission request:
Once dismissed (i.e., post-rationale) the application will then request the permission as before, but with an additional option to mute further requests.
If the user does select the option to mute further permission requests then the program will still function if written correctly. When the permission is requested the callback will execute immediately stating the permission was denied. If the user wants to later change their mind they can do that - they just go to the application settings and set the permissions as required:
To get more of a flavour of how the permissions framework operates you should browse through the product demo projects, which are found in this folder (just enter that folder including the environment variable reference at the start into any Explorer address bar or any open dialog):
%PUBLIC%\Documents\Embarcadero\Studio\20.0\Samples\Object Pascal
Those that require runtime permissions have been updated accordingly and include:
In order to fully embrace all the new permissions-related rules and regulations coming from Google we have to be aware of more than just setting simple permissions (and requesting them appropriately). When Android 7.0 (Android Nougat) came along some security loopholes were tightened up and this has had a knock-on effect for any application targeting Android 7.0 or higher. Since Delphi 10.3 Rio now targets the Android 8.0 SDK level this affects Delphi applications too, so we need to pay attention.
The particular change affects one of the classes used to take a photograph with the camera and receive it in your application and this becomes an issue due to what the class needs to do and how it does it. In FireMonkey the TTakePhotoFromCameraAction
standard action component performs this task. The way the component works is to launch the camera application with an instruction to take a photo and store it in a file, whose identity (a file URI) is passed to the camera app in an an Android Intent
object. It is this latter point that now falls foul of the new rules. Android 7.0 no longer permits you to expose URIs of type file://
outside your application so the attempt to pass the reference to the camera app gets
quickly picked up and you get a security exception.
To operate within the permitted guidelines requires 2 steps.
file://
URI to a content://
URI.FileProvider
.The first step is mopped up in the FireMonkey Java RTL code. Unfortunately the second step, using a file provider, requires adding additional information to the application manifest file and also deploying an additional XML resource file. It's all a bit of a fiddly exercise but fortunately Delphi 10.3 Rio makes it completely straightforward with a new Android-specific application entitlement available in the project options Entitlement list.
Entitlements are really macro options. You enable a single entitlement and a number of things are done to your project as a result. In this case the manifest gets a <provider>
section, and a new provider_paths.xml resource file added to the Android resources folder tree.
If you use a TTakePhotoFromCameraAction
either explicitly or implicitly you need to enable the Secure File Sharing entitlement:
If you forget to enable the Secure File Sharing entitlement you will be greeted with this almost impenetrable runtime exception (I say almost impenetrable because there are various posted questions about it on various forums, all of which state that the required manifest file section and resource file must both both be present):
These product demos have been updated by having the Secure File Sharing entitlement enabled:
Earlier we looked at the advised approach of using the runtime permissions framework. This section represents the other way of doing it, the ill-advised way. It's only here for the sake of interest and completeness so you can skip it if not interested in the "wrong" approach.
CAUTION: remember the code in this section is really just informative and for comparison. It uses the low-level RTL subscription messaging system directly and skips the dedicated RTL permissions framework. You should really use the permissions framework instead!
To see how we can implement the request for and the response from runtime permissions let's assume we have an application similar to the one discussed above. It wants permission to know the user's location so that a TMapView
control can display user location controls on the map. In Android terms this means we need to have the ACCESS_COARSE_LOCATION
and/or ACCESS_FINE_LOCATION
permissions granted.
Without these permissions (actually both are not required, just one of them should be enough) the map is unable to ascertain the user's location and so the code logic wants to ensure the relevant properties (TMapLayerOption.UserLocation
in the LayerOptions
set property and TMapControlOption.MyLocation
in the ControlOptions
set property) are not added to the map if the user location permission(s) are not granted.
The code to request the permission from the user is shown below. The map setup code is tucked away in a routine called SetupMap
. If it is passed True
it adds in the user location options, if passed False
it ensures the user location options are not added.
We can see that if the permissions are already granted, or we are running on an OS older than Android 6.0 (SDK version 23, aka API level 23) then the SetupMap
routine is simply executed with a parameter value of True
. Otherwise we run the code to request the permissions and do nothing further until we know what the user has decided.
In order to respond to the user response to the runtime permission request we need to subscribe to the TPermissionsRequestResultMessage
message via the RTL messaging system as follows. If one of these TPermissionsRequestResultMessage
records is sent to subscribers then HandlePermissionRequest
will be called.
Here is HandlePermissionRequest
(taking advantage of the new inline variables and type inference also added in 10.3 Rio). The message record has its constituent fields broken out to then be passed along to a final helper routine, which is meant to be a Delphi equivalent to the Android onPermissionsRequest
callback.
As you can see the granted status of the permissions is examined and this then controls the parameter passed into the call to SetupMap
.
If the application goes down an execution path that requests permissions from the user who proceeds to deny them that is fine. We can act accordingly.
If the application later goes down the same execution path and again requests permissions from the user (and potentially again and again) without some form of additional explanation then this could get quite annoying or perhaps frustrating for the user.
Given this potential Google recommends taking this into account when asking second and subsequent times. This can be done quite straightforwardly if we use some classes from the Android Support Library.
If we do, then the Support Library code will keep track of whether the user has declined a permission request. If so then we can easily find out and so display an additional explanatory message to the user explaining why we want the permission. In other words the app can offer up a rationale as to why the permission is important and being asked for again.
This can all be achieved with use of the ActivityCompat
and ContextCompat
v4 Support Library classes as shown in this re-worked version of the permission-requesting code:
Google has changed the rules for submitting applications to the Google Play Store. They must target a recent API Level (26 or higher, i.e. Android 8.0), which has implications of requiring support for runtime permissions (as introduced in Android 6.0, API level 23).
Delphi 10.2 Tokyo was not able to support this requirement but the toolchain changes and RTL / FMX updates in 10.3 Rio mean that Delphi developers can work with this new dynamic permission request model. The runtime permissions framework added in the RTL is intended, going forward, to be a cross-platform permission management API. In its first iteration in Delphi 10.3 it actively supports only Android.
I should point out that I do realise that the my example wasn't the most marvellous example I could have chosen. I am aware that of all the things you can do in Android that require runtime permissions, such as taking a picture, sending an SMS, recording some audio and more besides, my example actually isn't at all brilliant for a clear reason.
The goal was to show runtime request of user location permission and illustrated in an example that wanted to tell a map control to add on user location UI if the permission was granted and not add the user location UI if the permission was denied. As it happens, the map control internally already looks at the permissions. If you've asked for the user location UI on the map control and the required permission is not granted then the map control simply won't display those controls. So in a sense the example logic is a little redundant.
However I ask you, the reader, to simply take the sample code as representative of how you approach runtime permission request and response-processing in any scenario. It's the same sort of code that you will find scattered through the sample applications that come with RAD Studio 10.3 Rio, and at least shows that the application is trying to make a point to the user that it really wants to display their location on the map control.
Go back to the top of this page