Android Permissions Control

Publication date:
Last update: 2021-12-31
Author:

 

In the early versions of Android, the developers were able to obtain sensitive permissions by just declaring them in the AndroidManifest.xml file. Currently, only the normal (less sensitive) permissions are available in this way. The more sensitive permissions, such as location, require explicit user approval. Some permissions, like SMS, require selective approval by the Google Play team to allow publishing on the store. Moreover, the applications signed by the mobile operator may have special privileges, known as carrier privileges. This feature allows configuring the device (for example, setting up VoLTE services). Permissions can be temporary or permanent. Additionally, some permissions may behave differently if the service is running in the background.

Android Permissions Control

 

Install-time permissions

Install-time permissions, as the name suggests, are granted when the user installs the app. An example is the permission to access the internet.

To request an install-time permission, just declare it in the AndroidManifest.xml file, for example:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 

These permissions are visible to the user in the online store. However, the user may approve the installation without paying much attention to these permissions. From a security point of view, this is fine for normal permissions with minimum impact.

Install-time permissions are granted permanently for the application lifecycle.

 

Runtime permissions

On the other hand, the most sensitive permissions require additional actions, besides the declaration within the XML. For example, let's try to request the location permissions:

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

The ACCESS_COARSE_LOCATION permission is necessary to get the approximate location of the device. Usually, this kind of location is provided by the network services. On the other hand, the ACCESS_FINE_LOCATION permission is necessary to obtain the accurate location, typically through the GPS. Google recommends including both permissions to let the end user decide, whether to approve coarse or fine location, which is possible since Android 12.

However, declaring these permissions is not enough. Since Android 6, these permissions must be requested at runtime. In this way, the user is more aware of sensitive permissions and can reject them when the app is in use.

Google recommends using the ContextCompat and ActivityCompat helper classes to validate and request permissions at runtime. For example:

import androidx.core.content.ContextCompat;
import androidx.core.app.ActivityCompat;

String COARSE = "android.permission.ACCESS_COARSE_LOCATION";
String FINE = "android.permission.ACCESS_FINE_LOCATION";
int LOCRESULT = 123;

Context ctx = getApplicationContext();
if(ContextCompat.checkSelfPermission(ctx, FINE) != 0 &&
   ContextCompat.checkSelfPermission(ctx, COARSE) != 0){
   ActivityCompat.requestPermissions(this,
      new String[]{ FINE }, LOCRESULT);
}
 

This code verifies if any of these two permissions was granted already. Otherwise, a popup will ask the user to grant the permissions. As you can see, the permission check uses the context associated with the application process. This is because internally Android associates the permissions with the process ID (PID) and the user ID (UID). On the other hand, the permissions request receives a parameter representing the current activity (Activity) because it can prompt the user to approve the permissions. When the user approves or disapproves the request, the outcome will be notified through the onRequestPermissionsResult method on this activity:

@Override
public void onRequestPermissionsResult(
   int req, String perms[], int[] res){
   if(req == LOCRESULT
      && res.length > 0 && res[0] == 0){
      // permissions granted
   }
}
 

Processing the result notification is optional. The user interaction is asynchronous. Therefore, processing the notification is the easiest way to implement a conditional code based on the user's decision. For example, if the user decides not to approve the permissions, the application could display an error message or fallback to an alternative functionality.

If you don't want to use the AndroidX library (Android Extension Library), it is possible to implement the same logic using the standard Android APIs. This option is not recommended by Google and may stop working in a future Android version.

import android.os.Process;
import android.app.Activity;

String COARSE = "android.permission.ACCESS_COARSE_LOCATION";
String FINE = "android.permission.ACCESS_FINE_LOCATION";
int LOCRESULT = 123;

int pid = Process.myPid(), uid = Process.myUid();
if(checkPermission(FINE, pid, uid) != 0 &&
   checkPermission(COARSE, pid, uid) != 0){
      requestPermissions(new String[]{ FINE }, LOCRESULT);
}
 

In both cases, it is important to check the SDK version and implement some error handling with a try/catch block, as follows:

if(Build.VERSION.SDK_INT > 22){
   try{
     // check the location permissions
   }catch(Exception exc){
      Log.e("App", "exception", exc);
   }
}
 

In this way, the permissions validation code is executed on Android 6 or later (SDK greater than 22), because in the previous versions these permissions were granted at installation time.

Keep in mind that since Android 12 it is recommended to separate the approximate location and precise location permissions. The user should be able to choose one of them, both or neither.

The same strategy can be used to request other runtime permissions. Prior to requesting permissions, it is recommended to display an information box explaining why these permissions are necessary. A descriptive message can help convincing the user to grant the permissions.

Runtime permissions can be revoked automatically, after a period of inactivity. This happens since Android 11. If the app is not used for a long time (several months), the permissions can be automatically revoked. Therefore, the application has to check the permissions every time they are needed.

 

Carrier privileges

Starting with Android 5.1, there is a feature known as Carrier Privileges. This feature provides special permissions to the applications signed by a certificate matching the keys loaded from the SIMcard. Normally only the mobile operator or the SIMcard manufacturer can load certificates into the SIMcard. An independent application provider doesn't know the SIMcard administrative keys to perform this operation. Therefore, having the ability to manipulate the SIMcard contents may unlock special Android permissions.

Actually, the SIMcard stores the hash value (SHA1) of the key, not the key value. Optionally, along with this hash, the name of the application's package can be included. It corresponds to the package parameter declared in the AndroidManifest.xml. This can be useful if there are multiple apps signed with the same certificate, but the carrier privileges are restricted to some of them.

There are two methods to store the list of signatures in the SIMcard:

  • Through an application called ARA running inside the SIMcard. This application is defined in the GPD_SPE_013 standard.1
  • In a file, called ARF defined in the PKCS #15 standard.2

ARF is supported since Android 7. It is used if ARA is not found. The ARA option is more widely used because it has higher precedence and is supported since Android 5.1.

Currently the vast majority of SIMcards support the Global Platform standards. It doesn't mean that the ARA application is available on all SIMcards. Not all operators use this optional feature. There is an open source ARA version here:

GitHub
The ARA-M application by Bertrand Martel is free and compatible with most SIMcards.

 

Usually the mobile operators prefer commercial ARA implementations supplied by the SIMcard manufacturers. One of the reasons is that generic applications can cause intensive use of the SIMcard's non-volatile memory shortening its lifespan.

GitHub
The CoIMS project contains a guide explaining how to install ARA-M and how to troubleshoot some common errors.

 

To install an application in the SIMcard, you must have the administrative keys. The problem is that test SIMcards, which are marketed for engineering purposes and include administrative keys, generally do not include a mobile subscription. In contrast, the SIMcards provided by mobile operators do not include administrative keys. Therefore, using this technology with a real mobile subscription is almost impossible without close cooperation with the mobile operator.

Some of the features available under carrier privileges are listed below:

  • Reading the mobile subscription ID (IMSI).
  • Changing the operator name.
  • Activating/deactivating the LTE, CDMA, GSM/WCDMA and other networks.
  • Setting up and modifying the IMS configuration (e.g., VoLTE, VoWiFi).

A more complete list is available on the official Android developer site, although the public list is not exhaustive. The carrier privileges cover sensitive runtime permissions, such as turning data services on or off, blocking networks, selecting the mobile network manually or automatically and exchanging APDU commands with the SIMcard, among others. The next article will cover the latter use case in more detail.

No special code is required to make use of the carrier privileges inside the app. These privileges are granted automatically if the SIMcard contains the hash of the key used to sign the app, and optionally the name of the package. The privileges are also automatically revoked if the SIMcard is removed.

The carrier privileges work even without mobile network registration. They work in airplane mode as well. If the phone supports dual-SIM or multi-SIM, Android tries to scan the ARA or ARF on all SIMcards available.

 

Conclusions

Android permissions management did evolve and became more restrictive. Although Android is an open operating system, not all the details regarding permission handling are public and documented.

If you need to use sensitive permissions and you can't get them for the latest Android version, switching to an older version can make it easier to get the necessary permissions. For example, reading the subscription ID (IMSI) in Android 1.6 was possible by simply declaring the READ_PHONE_STATE permission in the AndroidManifest.xml file.

If you're developing for mass use, it's important to reduce the use of permissions, especially sensitive permissions. Failing to do so increases security risks and affects the availability of the application (e.g., on Google Play).

 

References

  1. «Secure Element Access Control v1.1», Global Platform (2014)
  2. «Cryptographic Token Information Format Standard v1.1», RSA Laboratories (1999)