You just added a new system service and flashed the device. Instead of working, your feature crashes or fails silently. When you check the logs, you find a cryptic avc: denied message. Your service stepped outside its designated sandbox. The SELinux subsystem in the Linux kernel blocked the action to protect the system. Understanding how to read and resolve these Access Vector Cache (AVC) denials forms the primary debugging workflow for Android security policies.
Deciphering the Denial Message
Before you can fix an SELinux violation, you need to know exactly what the kernel rejected. The system does not explicitly tell you which policy file to edit. It simply dumps a raw AVC denial into the kernel ring buffer instead. You must manually parse this string using dmesg or logcat to extract the missing permissions. Let us look at a standard denial and translate it.
type=1400 audit(16234234.123:45): avc: denied { read } for pid=456 comm="mediaserver" name="test.mp4" dev="dm-1" ino=12345 scontext=u:r:mediaserver:s0 tcontext=u:object_r:app_data_file:s0 tclass=file permissive=0
Every denial provides the exact coordinates of the failure. The scontext (source context) tells you the domain of the process requesting access. In this case, the source is mediaserver. Next, the tcontext (target context) reveals the type of the object being targeted, which is app_data_file. Look at tclass to define the object category, like file or chr_file.
A denied { read } block specifies the exact permission missing from your policy. Finally, permissive=0 confirms the system actively blocked the action, rather than just logging it. This specific denial translates directly into the SELinux policy rule you need to write.
allow mediaserver app_data_file:file read;
Translating a single denial by hand is manageable. Bringing up a new device or writing a complex service can generate hundreds of these messages quickly. Manual translation becomes impossible at that scale.
Automating Rule Generation
To prevent engineers from wasting days parsing logs, AOSP includes a host utility called audit2allow. This tool reads raw AVC logs and automatically generates the corresponding policy rules required to satisfy them.
The following flowchart shows how this workflow converts raw kernel logs into actionable policy files. This visualizes the pipeline bridging your test device and your local build environment. Look for how audit2allow acts as a translation layer between kernel complaints and SELinux syntax.
The tool takes the raw text from the device and cross-references it against your compiled policy. It then outputs valid rules that you can copy into your source files. To use it, capture the kernel logs and pipe them directly into audit2allow.
adb shell dmesg | audit2allow -p out/target/product/<device>/root/sepolicy
Common Mistake: Running
audit2allowwithout the-pflag causes it to generate generic rules. It needs that flag to understand the context of your device's specific policy definitions.
If a tool can automatically generate missing rules, you might wonder why we do not just script it to fix everything automatically.
The Danger of Blanket Allows
Blindly trusting audit2allow output can destroy device security. The tool acts entirely blind to system security design. It only knows what a process attempted to do, not what the process should actually be allowed to do. If you blindly copy and paste its output, you risk tearing massive holes in the Android security model.
Imagine an untrusted application attempts to read a sensitive system configuration file. The kernel blocks the action and logs a denial. The audit2allow tool will happily generate an allow rule granting that exact access. Applying that rule compromises the entire device. You must always ask yourself why a specific domain needs access to a specific type.
When analyzing a denial, the requested permission might be correct, but the target label might be wrong. A process often needs access to a file, but the system might label that file with an overly broad context. Writing a rule to allow access to that broad context creates a severe security risk.
Suppose your new system service, my_service, is denied access to /data/misc/my_data.txt. The log shows the file is labeled with the generic system_data_file type. A bad fix applies a blanket allow for that broad type.
allow my_service system_data_file:file rw_file_perms;
Granting your service read and write access to all system data files violates the principle of least privilege. Such a broad rule will likely trigger a build failure due to strict AOSP neverallow rules. The correct fix involves defining a precise label for your specific data. This leaves the rest of the system secure.
# 1. Define a new type in your service's .te file
type my_service_data_file, file_type, data_file_type;
# 2. Assign the label in file_contexts
/data/misc/my_data\.txt u:object_r:my_service_data_file:s0
# 3. Add a narrow allow rule
allow my_service my_service_data_file:file rw_file_perms;
This approach restricts your service to exactly the files it needs. Analyzing AVC denials requires more than just making the errors disappear. You are actively designing the boundaries of your system, and precision is critical. When bringing up a new service, getting blocked constantly makes development hard, which brings us to permissive domains.