Handling ungranted permissions

If you are developing an app that requires a permission, you should make sure that the app can handle the case when the user doesn't grant the permission. When your app has a set of requested permissions specified in the bar-descriptor.xml file, the app asks the user to grant the permissions when the app starts.

The user may deny one or more of the requested permissions. Your app won't know that the user denied the permission until it tries to use the API that requires the permission. When an app tries to use an API without the necessary permissions, an error or error code is returned. This behavior helps to protect against the inadvertent use of functionality and potentially malicious code.

Note: Your app can't check which permissions have been granted while the app is running. Your app can only use the functions that require the permission and handle any errors that it encounters.

When an error that could be the result of an ungranted permission occurs, you can display a dialog box that explains the issue to the user and then directs the user to the Settings app with the Application Permissions screen opened. If the permission is critical to the functionality that your app is offering, a dialog box is a good choice because you can offer the user the option to change the permission immediately.

Screen showing a permissions dialog box in a Cascades app.

If the permission isn't necessary but provides additional functionality, you can provide feedback inline with a toast or you can decide to provide no feedback. For example, if your social media app shares the user's current location but the user hasn't granted the access_location_services permission, you can choose to not interrupt the UI with a dialog box because the functionality is not critical.

Note: It's also important to handle situations where a user may grant only a subset of permissions. For permissions that have subpermissions, you should also handle situations where you don't have all the permissions under a main permission.

QML

The following code sample shows how to attach a SystemDialog to your UI. The confirmButton is labeled "Launch Settings", and when the confirmButton is tapped, the Settings app is invoked. The displayPermissionError() function displays the SystemDialog when something goes wrong. This function can be reused for any error that might occur because a permission has not been granted by the user. When you call the function, you have to supply the text of the message that asks the user if there is a permission that has not been granted.

import bb.system 1.2
    
    // ...     
        
    // Attach a system dialog that is used to display
    // information about missing permissions.
    attachedObjects: [

        SystemDialog {
            id: errorDialog
            title: qsTr("Something went wrong.")
            confirmButton.label: qsTr("Launch Settings")

            onFinished: {
                if (value == SystemUiResult.ConfirmButtonSelection) {
                    // The app settings object can also 
                    // launch the system settings app.
                    _appSettings.invokeSystemSettings
                        ("settings://permissions");
                }
            }
        }
    ]

	// This function is called when something goes wrong 
    // because a permission was not granted.
    function displayPermissionError(error) {
        errorDialog.body = error + 
        	qsTr(" You have to restart the app " +
        		"after changing permissions.");
        errorDialog.show();
    }

For example, your app can call displayPermissionError() when your app fails to save a photo. In the following code sample, the photoSaveFailed() signal triggers a call to the function to display the dialog box:

                
onPhotoSaveFailed: {
    // The photo couldn't be saved.
    // It could be that the permission to 
    // access shared files was not granted. 
    // Display a dialog box since this is 
    // the most likely scenario.

    displayPermissionError(qsTr("The photo couldn't be saved. "
        + "Make sure that the access_shared permission is set "))
    photoTaken = false;
}

To see a sample app that uses these techniques to check for ungranted permissions, download the Rundgang sample. To learn more about system dialogs, see Dialog boxes, prompts, and toasts and review the UI guidelines for dialog boxes and toasts. To learn more about invoking the Settings app, see Invoking core applications.

C++

We're currently working on updating this section.

C

The following code sample illustrates a way to check for permissions. The code provides an illustration of how to check the error code when your app doesn't have the proper permissions for the Camera API. The error codes are different for different APIs.

If your app tries to use the camera but your app didn't request the use_camera permission, an error code is returned when you call a function from the Camera library. For this reason, you should always write your application logic to handle situations when the necessary permissions aren't available.

#include <assert.h>
#include <bps/bps.h>
#include <bps/dialog.h>
#include <bps/event.h>
#include <bps/navigator.h>
#include <bps/screen.h>
#include <screen/screen.h>
#include <camera/camera_api.h>
                    
#define APP_ZORDER (100)
                    
typedef enum { STATE_STARTUP = 0,
               STATE_CAMERA_READY,
               STATE_VIEWFINDER,
               STATE_TAKINGPHOTO,
               STATE_NOPERMISSION
               } state_t;
                    
static state_t status = STATE_STARTUP;
static dialog_instance_t alert_dialog = NULL;
static bool shutdown = false;
static screen_context_t screen_ctx;
                    
static const char vf_group[] = "viewfinder_window_group";
static camera_handle_t handle = CAMERA_HANDLE_INVALID;
static int main_bps_chid = -1;
                    
char* messageok = "Good! You have the permissions to use the camera.";
char* messagenopermissions = "Oh no! You do not have permission to use the camera. "
                             "To change the permissions, go to Settings > Security and "
                             "Privacy > Application Permissions and grant the Camera "
                             "permission to this app. Close this app and restart it after "
                             "you have granted the necessary permissions.";
                    
// NOTE: In this code sample, we are purposely ignoring some error return codes
// for the sake of clarity. In production level code, check the return codes for errors
// to help isolate bugs with your app
static void check_permission()
{
   camera_error_t err;
   unsigned int num;
   unsigned int i;
   camera_unit_t cams[CAMERA_UNIT_NUM_UNITS];
   camera_unit_t unit;
                    
   // Select a camera unit
   unit = CAMERA_UNIT_REAR;
   fprintf(stderr, "selecting camera unit %d\n", unit);
   err = camera_open(unit,
                     CAMERA_MODE_RW | CAMERA_MODE_ROLL,
                     &handle);
                    
   if (err == CAMERA_EOK) {
      // Set the message to indicate that you have the required permissions.
      // In production code, an error message isn't necessary
      status= STATE_CAMERA_READY;
   }
                    
   if (err == CAMERA_EACCESS){
      // Set the state to indicate that the required permissions are not available
      fprintf(stderr, "camera_open() - no permission: %d\n", err);
      status=STATE_NOPERMISSION;
   }
   return;
}
                    
// This function is used to show the dialog box with the error messages
static int setAndDisplayDialog()
{      
   // Set the message to display to determine whether you have permissions
   char* displaymessage = "";
   if (status == STATE_CAMERA_READY)
       displaymessage = messageok;
   else if (status == STATE_NOPERMISSION)
       displaymessage = messagenopermissions;
   else
       displaymessage = "Not a permission issue.";
       
   // Create a dialog box to display the error message.
   if (dialog_create_alert(&alert_dialog) != BPS_SUCCESS) {
       fprintf(stderr, "Failed to create alert dialog.\n");
       return -1;
   }
                    
   if (dialog_set_alert_message_text(alert_dialog, displaymessage) != BPS_SUCCESS) {
       fprintf(stderr, "Failed to set alert dialog message text.\n");
       dialog_destroy(alert_dialog);
       alert_dialog = 0;
   }
                    
   if (dialog_add_button (alert_dialog, DIALOG_CANCEL_LABEL, 
                          true, NULL, true) != BPS_SUCCESS) {
       fprintf(stderr, "Failed to add button to alert dialog.\n");
       dialog_destroy(alert_dialog);
       alert_dialog = 0;
       return -1;
   }
                    
   if (dialog_show(alert_dialog) != BPS_SUCCESS) {
       fprintf(stderr, "Failed to show alert dialog.\n");
       dialog_destroy(alert_dialog);
       alert_dialog = 0;
   }
                    
}
                    
int main(int argc, char **argv) {
                    
   const int usage = SCREEN_USAGE_NATIVE;
   status = STATE_STARTUP;
   screen_window_t screen_win;
   screen_buffer_t screen_buf = NULL;
   int rect[4] = { 0, 0, 0, 0 };
   int i = APP_ZORDER;
   
   // Create an application window that acts as a background
   screen_create_context(&screen_ctx, 0);
   screen_create_window(&screen_win, screen_ctx);
   screen_create_window_group(screen_win, vf_group);
   screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &usage);
   screen_create_window_buffers(screen_win, 1);
   screen_get_window_property_pv(screen_win, 
                                 SCREEN_PROPERTY_RENDER_BUFFERS,
                                (void **)&screen_buf);
   screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, rect+2);
                    
   // Fill the window with black
   int attribs[] = { SCREEN_BLIT_COLOR, 0x00000000, SCREEN_BLIT_END };
   screen_fill(screen_ctx, screen_buf, attribs);
   screen_post_window(screen_win, screen_buf, 1, rect, 0);
   
   // Position the window at an arbitrary z-order
   screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_ZORDER, &i);
                    
   // Request that navigator, dialog, and screen events are sent
   bps_initialize();
   main_bps_chid = bps_channel_get_active();
   screen_request_events(screen_ctx);
   
   // Request that events are sent. A value of 0 indicates that 
   // all events are requested
   dialog_request_events(0);
   navigator_request_events(0);
 
   // Call the function to check for permissions
   check_permission();
   setAndDisplayDialog();
                    
   while (!shutdown) {
      bps_event_t *event = NULL;
   
       // The value of -1 means that the function waits
       // for an event before returning
       bps_get_event(&event, -1);
   
       // Event loop to receive BPS events. In this scenario,
       // receive an event to display the dialog box
       if (event) {
           if (bps_event_get_domain(event) == dialog_get_domain()) {
               int selectedIndex =
                   dialog_event_get_selected_index(event);
                const char* label =
                   dialog_event_get_selected_label(event);
                 const char* context =
                   dialog_event_get_selected_context(event);
                    
           // Further handle and process dialog event here.
           //...
         }
         //
         // App logic in the event loop
         // ...
         //
     }
     //
     // App logic in the event loop
     // ...
     //
   }
                    
   // Clean up when the app is closed or the main loop exits
   screen_stop_events(screen_ctx);
   bps_shutdown();
   screen_destroy_window(screen_win);
   screen_destroy_context(screen_ctx);
   return 0;
}   

Each API handles permissions differently. You should look at every API your app uses that requires a permission and find out what errors you get when the permission has not been granted.

For example, in Cascades, the CalendarService class requires that the access_pimdomain_calendars permission is granted to construct a new CalendarService. If you try to call any CalendarService functions on an invalid object, your app will fail. In this case, you should probably check that the CalendarService object was created successfully before you do anything with it. If the CalendarService object was not created successfully, you could display a SystemDialog asking the user if the access_pimdomain_calendars permission was set.

If you are using the Camera class, you can create a Camera object successfully whether or not the use_camera permission is granted. It’s only when your app calls Camera::open() that the use_camera permission is necessary. To handle errors that occur when Camera::open() is called, you should listen for the cameraOpenedFailed() signal, and then check the error code to see why it failed. The camera might not open because the use_camera permission has not been granted. You can display a SystemDialog for this type of failure. For more information about finding supported and accessible cameras, see Opening the camera.