Skip to content

Permissions and components

Permissions

An app can request permissions in two tags and define its own in a third:

Tag Method Meaning
<uses-permission> get_permissions() requested on every Android version
<uses-permission-sdk-23> get_permissions_sdk23() requested only on Android 6.0+
<permission> get_declared_permissions() defined by this app for others to request
for permission in sorted(apk.get_permissions()):
    print(f"uses-permission         {permission}")

for permission in apk.get_permissions_sdk23():
    print(f"uses-permission-sdk-23  {permission}")

# permissions the app defines for others to request
for permission in apk.get_declared_permissions():
    print(f"permission              {permission.name} [{permission.protection_level}]")
for permission in apk.get_permissions() {
    println!("uses-permission         {permission}");
}
for permission in apk.get_permissions_sdk23() {
    println!("uses-permission-sdk-23  {permission}");
}
for permission in apk.get_declared_permissions() {
    println!(
        "permission              {:?} [{:?}]",
        permission.name, permission.protection_level
    );
}
apk-info axml app.apk | grep -E '<uses-permission|<permission'
uses-permission         android.permission.FOREGROUND_SERVICE_DATA_SYNC
uses-permission         android.permission.INTERNET
uses-permission         android.permission.RECEIVE_BOOT_COMPLETED
uses-permission-sdk-23  android.permission.POST_NOTIFICATIONS
permission              io.github.delvinru.apkstest.READ [signature]

Exported components

Other apps can start exported activities, services and receivers, and query exported providers. Start with them when you assess an app's attack surface.

target_sdk = apk.get_target_sdk_version()


def is_exported(component) -> bool:
    # before Android 12 (target SDK 31), a component with an intent filter
    # was exported unless it said otherwise
    if component.exported is None:
        return bool(component.intent_filters) and target_sdk < 31
    return component.exported == "true"


for kind, components in [
    ("activity", apk.get_activities()),
    ("service", apk.get_services()),
    ("receiver", apk.get_receivers()),
]:
    for component in components:
        if is_exported(component):
            print(f"{kind:9} {component.name}  permission={component.permission}")

for provider in apk.get_providers():
    if provider.exported == "true":
        print(f"provider  {provider.name}  authorities={provider.authorities}")
let target_sdk = apk.get_target_sdk_version();

// before Android 12 (target SDK 31), a component with an intent filter
// was exported unless it said otherwise
let is_exported = |exported: Option<&str>, has_filters: bool| match exported {
    Some(value) => value == "true",
    None => has_filters && target_sdk < 31,
};

for activity in apk.get_activities() {
    if is_exported(activity.exported, !activity.intent_filters.is_empty()) {
        println!("activity  {:?}", activity.name);
    }
}
for service in apk.get_services() {
    if is_exported(service.exported, !service.intent_filters.is_empty()) {
        println!("service   {:?}", service.name);
    }
}
for receiver in apk.get_receivers() {
    if is_exported(receiver.exported, !receiver.intent_filters.is_empty()) {
        println!("receiver  {:?}", receiver.name);
    }
}
for provider in apk.get_providers() {
    if provider.exported == Some("true") {
        println!("provider  {:?} {:?}", provider.name, provider.authorities);
    }
}
activity  .MainActivity  permission=None
receiver  .BootReceiver  permission=None

exported is the raw attribute value: "true", "false" or None. When it's None, Android decides:

  • Activities, services, receivers. Before Android 12 (target SDK 31), a component with an <intent-filter> is exported by default. Since target SDK 31, a component with an intent filter must set android:exported, or the app won't install.
  • Providers. Exported by default only when minSdkVersion or targetSdkVersion is 16 or lower.

If a component requires a permission with the signature protection level, only apps signed with the same key can reach it.

Intent filters

Activities, activity aliases, services and receivers carry their intent filters as lists of actions and categories:

for activity in apk.get_activities():
    for intent_filter in activity.intent_filters:
        if "android.intent.action.VIEW" in intent_filter.actions:
            print(f"{activity.name} handles VIEW: {intent_filter.categories}")
.MainActivity handles VIEW: ['android.intent.category.DEFAULT', 'android.intent.category.BROWSABLE']

All component fields

get_activities(), get_activity_aliases(), get_services(), get_receivers() and get_providers() return objects with the raw manifest attributes: name, exported, enabled, permission, process and so on, plus type-specific ones such as authorities and grant_uri_permissions for providers or foreground_service_type for services. See the Python API for the full list.