AOSP Framework & Internals
3 min read

Permission Control & SDK Integration

Learn about Permission Control & SDK Integration.

Exposing a new system service via a Manager API introduces a new attack surface. If your service performs sensitive operations (like accessing custom hardware or modifying system state), you must protect those APIs with Android permissions.

Defining Custom Permissions in AndroidManifest

The first step is defining the new permission. This is done in the framework's core manifest file: frameworks/base/core/res/AndroidManifest.xml.

<!-- Inside frameworks/base/core/res/AndroidManifest.xml -->
<permission android:name="android.permission.ACCESS_MY_CUSTOM_HW"
    android:protectionLevel="signature|privileged"
    android:label="@string/permlab_accessMyCustomHw"
    android:description="@string/permdesc_accessMyCustomHw" />

Setting the protectionLevel to signature|privileged ensures that only apps signed by the device manufacturer, or pre-installed in the privileged system partition, can acquire this permission. You must also define the string resources for the label and description in frameworks/base/core/res/res/values/strings.xml.

Enforcing Permissions in the Service

Defining the permission does not automatically protect your service. You must explicitly check the permission inside your service implementation before executing sensitive logic.

This is typically done by calling Context.enforceCallingOrSelfPermission().

package com.android.server.mycustom;

import android.content.Context;
import android.os.IMyCustomService;

public class MyCustomService extends IMyCustomService.Stub {
    private final Context mContext;

    public MyCustomService(Context context) {
        mContext = context;
    }

    @Override
    public void applyConfiguration(MyCustomConfig config) {
        // 1. Enforce permission FIRST
        mContext.enforceCallingOrSelfPermission(
                "android.permission.ACCESS_MY_CUSTOM_HW",
                "Requires ACCESS_MY_CUSTOM_HW permission");

        // 2. Perform the sensitive operation
        long token = Binder.clearCallingIdentity();
        try {
            // Interact with the HAL or lower-level system
            nativeApplyConfig(config);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
}

If the calling application does not hold the required permission, enforceCallingOrSelfPermission will throw a SecurityException, immediately aborting the Binder transaction.

Note the use of Binder.clearCallingIdentity(). When your service calls down into the HAL or kernel, those lower layers might check the UID of the caller. By default, the UID is the app that initiated the Binder call. Calling clearCallingIdentity() temporarily switches the UID context to system_server (the process hosting the service), allowing the operation to succeed, before restoring the original app's identity in the finally block.

CTS Tests for New Services

When you add a new service and API, especially if you modify public or system APIs, you should write tests to ensure they function correctly and that the permission checks are enforced.

While you cannot add tests to the official Google Compatibility Test Suite (CTS) for custom OEM features, you should utilize the same testing infrastructure (Tradefed) to write your own vendor test suite (VTS) or device-specific integration tests. A robust test will attempt to call the service without the permission and assert that a SecurityException is thrown, followed by a test that grants the permission and verifies the expected behavior.

Previous Lesson
API Exposure
Course Complete!
You've finished all lessons.