Android Custom Permissions

Budhdi Sharma
Android Champ
Published in
6 min readApr 8, 2024

--

Introduction

In the world of Android development, system services play a crucial role in providing core functionalities and managing sensitive data. As developers, we have a responsibility to ensure that these system-level components are properly secured and accessible only to authorised entities. One powerful tool at our disposal is the implementation of custom permissions, which allow us to define and enforce fine-grained access control within our Android applications.

In this article, we’ll take a deep dive into the process of adding custom permissions to secure a phone-related system service, providing a reusable template that can be applied to a wide range of system services. By the end of this guide, you’ll have a solid understanding of the necessary steps, best practices, and considerations to ensure the secure integration of custom permissions in your Android projects.

Understanding the Need for Custom Permissions

Android’s permission system is a fundamental security feature that allows users to control and manage the access granted to applications. While the Android platform comes with a set of predefined permissions, there are often use cases where existing permissions do not sufficiently cover the security requirements of your system services.

This is where custom permissions come into play. Custom permissions enable you, as a developer, to define and enforce your own access control mechanisms, tailored to the specific needs of your system service. By implementing custom permissions, you can ensure that sensitive functionalities and data are only accessible to authorised components within your Android ecosystem, enhancing the overall security and integrity of your system.

Note: The code example provided in this article is for illustrative purposes only and may not represent a real-world use case. The file paths and class names used are similar for explanation, but they may differ in your actual Android project.

Step 1: Defining the Custom Permission

Let’s start by defining the custom permission that will be used to protect our phone-related system service. In the Phone.java file (or an equivalent file for your specific system service or any of the custom property), we'll add the following code:

  • packages/services/Phone/phone-lib/src/android/phone/Phone.java
public final class Phone {
/**
* Custom Permission.
* @hide
*/
@AddedInOrBefore(majorVersion = 33)
public static final String PERMISSION_PHONE_CUSTOM = "android.phone.permission.PHONE_CUSTOM";
}

In this example, we’ve defined a custom permission called PERMISSION_PHONE_CUSTOM that will be used to control access to the PhoneServiceor can be considered reading any custom property. It's important to note that the permission name should follow the Android naming convention, starting with "android." to ensure compatibility and proper identification by the system.

Step 2: Associating the Custom Permission with Service Attributes

Now that we have defined the custom permission, we need to associate it with the specific attributes or properties within the PhoneService. In the PhonePropertyIds.java file (or a similar file), we'll add the necessary permissions:

  • packages/services/Phone/phone-lib/src/android/phone/PhonePropertyIds.java
public final class PhonePropertyIds {
@RequiresPermission(Phone.PERMISSION_PHONE_CUSTOM)
@AddedInOrBefore(majorVersion = 33)
public static final int PHONE_TEST_PROPERTY = 289410896;

@RequiresPermission(Phone.PERMISSION_PHONE_CUSTOM)
@AddedInOrBefore(majorVersion = 33)
public static final int PHONE_CALL_HISTORY = 289410897;
}

In this example, we’ve added two properties, PHONE_TEST_PROPERTY and PHONE_CALL_HISTORY, and associated them with the PERMISSION_PHONE_CUSTOM permission. This ensures that access to these properties is protected and can only be granted to components that have the custom permission.

  • packages/services/Par/phone-lib/src/com/android/phone/internal/PropertyPermissionMapping.java
public final class PropertyPermissionMapping {
public PropertyPermissionMapping() {
// Add propertyId and read/write permissions
// Phone Properties
map(Phone.PERMISSION_PHONE_CUSTOM, PhonePropertyIds.PHONE_TEST_PROPERTY);
map(Phone.PERMISSION_PHONE_CUSTOM, PhonePropertyIds.PHONE_CALL_HISTORY);
}
}
  • packages/services/Phone/service/src/com/android/phone/hal/PropertyHalServiceIds.java
public class PropertyHalServiceIds {
public PropertyHalServiceIds() {
mPropToValidBitFlag = new HashMap<>();
// Add propertyId and read/write permissions
// Phone Properties
mProps.put(PhoneProperty.PHONE_TEST_PROPERTY, new Pair<>(
Phone.PERMISSION_PHONE_CUSTOM,
Phone.PERMISSION_PHONE_CUSTOM));
mProps.put(PhoneProperty.PHONE_CALL_HISTORY, new Pair<>(
Phone.PERMISSION_PHONE_CUSTOM,
Phone.PERMISSION_PHONE_CUSTOM));
}
}

Step 3: Defining Permissions in the Manifest

The next step is to declare the custom permission in the AndroidManifest.xml file of the system service:

<permission android:name="android.phone.permission.PHONE_CUSTOM"
android:protectionLevel="signature|privileged"
android:label="@string/phone_permission_label_custom"
android:description="@string/phone_permission_desc_custom"/>

In this declaration, we specify the name of the custom permission, the protection level, and provide descriptive labels and descriptions. The protection level signature|privileged ensures that only system components signed with the same certificate as the system service can be granted this permission, maintaining a high level of security.

Step 4: Compiling the System Service

After making the necessary changes, you’ll need to compile the system service. This process may involve generating new API signatures and updating the current API files. Consult the specific instructions for your Android platform and development environment.

Return to the root directory mmm packages/services/Phone/.

During the compilation process, you may encounter errors related to the custom permission and associated properties. To resolve these issues, you’ll need to update the current API files to reflect the changes. This can be done by running the following commands in the root directory of your Android project:

m android.phone-stubs-docs-update-current-api
m android.phone-system-stubs-docs-update-current-api
m android.phone-test-stubs-docs-update-current-api

After execution, new content will be added to the current.txt file (no manual modification is required)

package android.phone {
field @RequiresPermission("android.phone.permission.PHONE_CUSTOM")
public static final int PHONE_TEST_PROPERTY = 289410896;
// 0x11200f48
//----------------------similarly for call history -------->
}

verify

Go back to the project root directory and use mthe command to recompile the entire project.

Use to make emu_img_ziprecompile the emulator image.

The compiled image file is here out/target/product/emulator_phone_x86_64/sdk-repo-linux-system-images-eng.$(whoami).zip.

Copy the generated zip file, unzip it and replace the image in the local SDK ~/Library/Android/sdk/system-images/android-33/android-phone/x86_64, then install the simulator wipe dataand restart the simulator.

Step 5: Enforcing the Custom Permission

With the custom permission defined and the system service compiled, you can now enforce the protection of the PhoneService and its associated properties. In your client code, you'll need to request the PERMISSION_PHONE_CUSTOM permission to access the protected functionality or attributes.

// Request the custom permission
if (ContextCompat.checkSelfPermission(this, Phone.PERMISSION_PHONE_CUSTOM) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Phone.PERMISSION_PHONE_CUSTOM}, REQUEST_CODE);
} else {
// Access the protected functionality or attributes
PhoneService.setTestProperty(value);
PhoneService.getCallHistory();
}

In this example, we first check if the client application has the PERMISSION_PHONE_CUSTOM permission. If the permission is not granted, we request it from the user. Once the permission is obtained, the client can access the protected functionality and attributes of the PhoneService.

Considerations and Best Practices

When implementing custom permissions for your Android system services, there are several important considerations and best practices to keep in mind:

  1. Carefully Evaluate the Need for Custom Permissions: Ensure that the use of custom permissions is genuinely necessary and that the existing Android permissions cannot sufficiently address your security requirements.
  2. Maintain Consistency: Align the naming convention and structure of your custom permissions with the Android platform’s guidelines to ensure seamless integration and recognition by the system.
  3. Enforce Strict Protection Levels: Use the signature|privileged protection level to limit access to only system components signed with the same certificate as the system service.
  4. Document and Communicate: Provide clear documentation and guidelines for your custom permissions, including their purpose, usage, and any potential impacts on client applications.
  5. Monitor and Maintain: Regularly review and update your custom permissions as your system service evolves, ensuring that the security measures remain effective and aligned with the overall Android ecosystem.

Conclusion

In this comprehensive guide, you’ve learned the step-by-step process of adding custom permissions to secure your Android system services. By defining, associating, and enforcing custom permissions, you can ensure that sensitive functionalities and data are only accessible to authorised components within your Android ecosystem, enhancing the overall security and integrity of your system.

Remember, custom permissions are a powerful tool in your Android development arsenal, but their implementation should be carefully considered and aligned with best practices. By following the guidelines and principles outlined in this article, you’ll be well on your way to building secure and robust Android system services that safeguard your users’ data and trust.

--

--

Budhdi Sharma
Android Champ

As an AOSP developer, I specialize in creating robust framework and system applications that seamlessly integrate with embedded systems on various SOCs