Runtime permissions in Android (new in C++ 10.3)

Brian Long Consultancy & Training Services Ltd.
November 2018

Accompanying source files are available to download.

Contents

Android Delphi

Introduction

RAD Studio's C++'s Android support has included support for application permissions since its introduction in XE6. 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:

Permissions in Delphi 10.2.3

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:

<%uses-permission%>

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:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Permissions requested this way are established/verified during application installation for applications built with C++ 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 C++Builder 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. C++ 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 C++ apps will now need to work with the runtime permissions model, something they could not do until C++ 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 C++ 10. Rio3.

Implementing runtime permissions in C++

At the lowest level Android runtime permissions require a couple of things:

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 C++ 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 C++ 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 C++ 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.

Runtime permissions through the RTL permissions framework (advised approach)

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:

  1. 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.

    Specifying your Google API key

  2. 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):

    <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="ENTER_YOUR_GOOGLE_MAPS_API_KEY_HERE" />
    <meta-data android:name="com.google.android.gms.version" android:value="7095000" />  

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:

Permissions in Delphi 10.3

The simple, initial approach

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.

#include <System.Permissions.hpp>
#ifdef __ANDROID__
    #include <Androidapi.Helpers.hpp>
    #include <Androidapi.JNI.Os.hpp>
#endif
...
    String FPermissionCoarseLocation;
    String FPermissionFineLocation;
...
__fastcall TMainForm::TMainForm(TComponent* Owner)
    : TForm(Owner)
{
#ifdef __ANDROID__
    // Note we can alternatively use literal strings:
    //   "android.permission.ACCESS_COARSE_LOCATION"
    //   "android.permission.ACCESS_FINE_LOCATION"
    // according to the documentation:
    //   https://developer.android.com/reference/android/Manifest.permission#ACCESS_COARSE_LOCATION
    //   https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION
    FPermissionCoarseLocation = JStringToString(TJManifest_permission::JavaClass->ACCESS_COARSE_LOCATION);
    FPermissionFineLocation = JStringToString(TJManifest_permission::JavaClass->ACCESS_FINE_LOCATION);
#endif
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::Button1Click(TObject *Sender)
{
    DynamicArray<String> permissions;
    permissions.Length = 2;
    permissions[0] = FPermissionCoarseLocation;
    permissions[1] = FPermissionFineLocation;

    PermissionsService()->RequestPermissions(permissions, LocationPermissionRequestResult);
}

With this the user will see something like:

Runtime permission request

When the user does respond to the permission request the information about that will be passed into the routine LocationPermissionRequestResult(). Here you can see that the aforementioned SetupMap() routine is now called with a bool value appropriate to the permission grant status.

#ifdef __ANDROID__
    #include <Androidapi.Jni.Widget.hpp>
    #include <FMX.Helpers.Android.hpp>
#endif
...
#ifdef __ANDROID__
void __fastcall TMainForm::Toast(String Msg, int Duration)
{
    CallInUIThread(
        [Msg, Duration]()
        {
            TJToast::JavaClass->makeText(
                TAndroidHelper::Context,
                StrToJCharSequence(Msg),
                Duration)->show();
        });
}
#endif

void __fastcall TMainForm::LocationPermissionRequestResult(TObject *Sender, const DynamicArray<String> APermissions, const DynamicArray<TPermissionStatus> AGrantResults)
{
    // 2 permissions involved: ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION
    bool LocationPermissionGranted = (AGrantResults.Length == 2) &&
         (AGrantResults[0] == TPermissionStatus::Granted) &&
         (AGrantResults[1] == TPermissionStatus::Granted);
    if (LocationPermissionGranted)
        Toast("User granted permission", TJToast::JavaClass->LENGTH_SHORT);
    else
        Toast("User denied permission", TJToast::JavaClass->LENGTH_SHORT);
    SetupMap(LocationPermissionGranted);
}

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 function as a parameter. This is demonstrated by some of the demo apps, including the one in CPP/Mobile Snippets/Location, which has this code within it:

void __fastcall TForm2::swLocationSensorActiveSwitch(TObject *Sender)
{
#ifdef __ANDROID__
    if (swLocationSensorActive->IsChecked)
    {
        DynamicArray<String> permissions;
        permissions.Length = 1;
        permissions[0] = JStringToString(TJManifest_permission::JavaClass->ACCESS_FINE_LOCATION);

        PermissionsService()->RequestPermissions(permissions,
            [this](const DynamicArray<String> APermissions, const DynamicArray<TPermissionStatus> AGrantResults)
            {
                if ((AGrantResults.Length == 1) and(AGrantResults[0] == TPermissionStatus::Granted))
                    // activate or deactivate the location sensor
                    this->LocationSensor1->Active = true;
                else
                {
                    this->swLocationSensorActive->IsChecked = False;
                    ShowMessage("Location permission not granted");
                }
            });
    }
    else
#endif
        // activate or deactivate the location sensor
        LocationSensor1->Active = false;
}

The more involved, and more correct approach

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 function reference that can be passed as shown below, which can be an anonymous function. 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 function reference 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.

#include <System.Permissions.hpp>
#include <FMX.DialogService.hpp>
...
void __fastcall TMainForm::Button1Click(TObject *Sender)
{
    DynamicArray<String> permissions;
    permissions.Length = 2;
    permissions[0] = FPermissionCoarseLocation;
    permissions[1] = FPermissionFineLocation;

    PermissionsService()->RequestPermissions(permissions, LocationPermissionRequestResult, DisplayRationale);
}
//---------------------------------------------------------------------------
// Optional rationale display routine to display permission requirement rationale to the user
void __fastcall TMainForm::DisplayRationale(TObject *Sender, const DynamicArray<String> APermissions, const _di_TProc APostRationaleProc)
{
    // Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response!
    // After the user sees the explanation, invoke the post-rationale routine to request the permissions
    TDialogService::ShowMessage("The app can show where you are on the map if you give it permission",
        [APostRationaleProc](TModalResult AKey)
        {
            APostRationaleProc->Invoke();
        });
}

The user sees the rationale message ahead of any subsequent permission request:

Runtime permission rationale

Once dismissed (i.e, post-rationale) the application will then request the permission as before, but with an additional option to mute further requests.

Subsequent runtime permission request

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:

App permission settings

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\CPP

Those that require runtime permissions have been updated accordingly and include:

Camera actions and file sharing

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 C++ 10.3 Rio now targets the Android 8.0 SDK level this affects C++ 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.

  1. The first step is for the URI to be changed from a file:// URI to a content:// URI.
  2. The second step is to permit the camera app temporary access to the resource behind that URI using a 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 C++ 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:

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):

Secure File Sharing entitlement not enabled

Secure File Sharing entitlement not enabled

These product demos have been updated by having the Secure File Sharing entitlement enabled:

Runtime permissions through the Android callback (ill-advised approach)

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 simple, initial approach

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.

#include <Androidapi.JniBridge.hpp>
#include <Androidapi.Helpers.hpp>
#include <Androidapi.Jni.Os.hpp>
...
    const int MY_PERMISSIONS_REQUEST_ACCESS_LOCATION = 1;
...
void TMainForm::RequestPermissions()
{
    TJavaObjectArray__1<_di_JString> *Perms = new TJavaObjectArray__1<_di_JString>(2);
    Perms->Items[0] = TJManifest_permission::JavaClass->ACCESS_COARSE_LOCATION;
    Perms->Items[1] = TJManifest_permission::JavaClass->ACCESS_FINE_LOCATION;
    // Since this whole procedure is only called in an API-guarded manner (from Button1Click),
    // there is no need to check the OS version here
    TAndroidHelper::Activity->requestPermissions(Perms, MY_PERMISSIONS_REQUEST_ACCESS_LOCATION);
    // MY_PERMISSIONS_REQUEST_ACCESS_LOCATION is an app-defined int constant.
    // The callback method, OnPermissionsRequest, gets the result of the request.
}

void __fastcall TMainForm::Button1Click(TObject *Sender)
{
    // API 23+ calls guarded by a runtime check against the OS SDK version
    if (TJBuild_VERSION::JavaClass->SDK_INT >= 23)
    {
        if ((TAndroidHelper::Activity->checkSelfPermission(TJManifest_permission::JavaClass->ACCESS_COARSE_LOCATION) !=
             TJPackageManager::JavaClass->PERMISSION_GRANTED) &&
            (TAndroidHelper::Activity->checkSelfPermission(TJManifest_permission::JavaClass->ACCESS_FINE_LOCATION) !=
             TJPackageManager::JavaClass->PERMISSION_GRANTED))
            RequestPermissions();
        else // Permission has already been granted
            SetupMap(true);
    }
    else
        SetupMap(true);
}

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.

#include <System.Messaging.hpp>
#include <Androidapi.Helpers.hpp>
...
__fastcall TMainForm::TMainForm(TComponent* Owner)
    : TForm(Owner)
{
    TMessageManager::DefaultManager->SubscribeToMessage(
        __classid(TPermissionsRequestResultMessage), HandlePermissionsRequest);

}

__fastcall TMainForm::~TMainForm()
{
    TMessageManager::DefaultManager->Unsubscribe(
        __classid(TPermissionsRequestResultMessage), HandlePermissionsRequest);
}

Here is HandlePermissionRequest(). 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.

#include <System.Messaging.hpp>
#include <Androidapi.Jni.Widget.hpp>
#include <Androidapi.Jni.JavaTypes.hpp>
#include <Androidapi.JniBridge.hpp>
#include <FMX.Helpers.Android.hpp>
...
void __fastcall TMainForm::Toast(String Msg, int Duration)
{
    CallInUIThread(
        [Msg, Duration]()
        {
            TJToast::JavaClass->makeText(
                TAndroidHelper::Context,
                StrToJCharSequence(Msg),
                Duration)->show();
        });
}

void __fastcall TMainForm::HandlePermissionsRequest(TObject *Sender, TMessageBase *M)
{
    TPermissionsRequestResultMessage *Msg = dynamic_cast<TPermissionsRequestResultMessage *>(M);
    if (Msg)
    {
        TPermissionsRequestResultData MessageData = Msg->Value;
        OnPermissionsRequest(MessageData.RequestCode, MessageData.Permissions, MessageData.GrantResults);
    }
}

void __fastcall TMainForm::OnPermissionsRequest(int ARequestCode,
    TJavaObjectArray__1<_di_JString> *APermissions, TJavaArray__1<int> *AGrantResults)
{
    if (ARequestCode == MY_PERMISSIONS_REQUEST_ACCESS_LOCATION)
    {
        bool LocationPermissionGranted =
            (AGrantResults->Length == 2) &&
            (AGrantResults->Items[0] == TJPackageManager::JavaClass->PERMISSION_GRANTED) &&
            (AGrantResults->Items[1] == TJPackageManager::JavaClass->PERMISSION_GRANTED);
        if (LocationPermissionGranted)
            Toast("User granted permission", TJToast::JavaClass->LENGTH_SHORT);
        else
            Toast("User denied permission", TJToast::JavaClass->LENGTH_SHORT);

        SetupMap(LocationPermissionGranted);
    }
}

As you can see the granted status of the permissions is examined and this then controls the parameter passed into the call to SetupMap().

The more involved, and more correct approach

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:

#include <FMX.DialogService.Async.hpp>
#include <Androidapi.Jni.Os.hpp>
#include <Androidapi.Jni.Support.hpp>
#include <Androidapi.Helpers.hpp>
...
    const int MY_PERMISSIONS_REQUEST_ACCESS_LOCATION = 1;
...
void __fastcall TMainForm::Button1Click(TObject *Sender)
{
    // Using helper class from Android Support Library, so no OS version checking required
    if ((TJContextCompat::JavaClass->checkSelfPermission(
            TAndroidHelper::Activity, TJManifest_permission::JavaClass->ACCESS_COARSE_LOCATION) !=
         TJPackageManager::JavaClass->PERMISSION_GRANTED) &&
        (TJContextCompat::JavaClass->checkSelfPermission(
            TAndroidHelper::Activity, TJManifest_permission::JavaClass->ACCESS_FINE_LOCATION) !=
         TJPackageManager::JavaClass->PERMISSION_GRANTED))
    {
        // Permissions are not granted.
        // Should we show an explanation?
        if (TJActivityCompat::JavaClass->shouldShowRequestPermissionRationale(
                TAndroidHelper::Activity, TJManifest_permission::JavaClass->ACCESS_COARSE_LOCATION) ||
            TJActivityCompat::JavaClass->shouldShowRequestPermissionRationale(
                TAndroidHelper::Activity, TJManifest_permission::JavaClass->ACCESS_FINE_LOCATION))
        {
            // Show an explanation to the user *asynchronously* -- don't block this thread waiting for the user's response!
            // After the user sees the explanation, try again to request the permission.
            TDialogServiceAsync::ShowMessage("The device location allows this app to show where you are on the map",
                [this](TModalResult AKey)
                {
                    RequestPermissions();
                });
        }
        else
            RequestPermissions();

    }
    else // Permission has already been granted
        SetupMap(true);
}

void TMainForm::RequestPermissions()
{
    TJavaObjectArray__1<_di_JString> *Perms = new TJavaObjectArray__1<_di_JString>(2);
    Perms->Items[0] = TJManifest_permission::JavaClass->ACCESS_COARSE_LOCATION;
    Perms->Items[1] = TJManifest_permission::JavaClass->ACCESS_FINE_LOCATION;
    // Using helper class from Android Support Library, so no OS version checking required
    TJActivityCompat::JavaClass->requestPermissions(TAndroidHelper::Activity, Perms, MY_PERMISSIONS_REQUEST_ACCESS_LOCATION);
    // MY_PERMISSIONS_REQUEST_ACCESS_LOCATION is an app-defined int constant.
    // The callback method, OnPermissionsRequest, gets the result of the request.
}

Conclusion

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).

C++ 10.2 Tokyo was not able to support this requirement but the toolchain changes and RTL / FMX updates in 10.3 Rio mean that C++ 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 C++ 10.3 it actively supports only Android.

Addendum

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