← Back to list

SELinux on Android: The “Secret Handshake” That Secures Your Service

If you’ve ever tried to add a custom daemon or a “Hello World” service to the Android /vendor partition, you’ve likely run into a brick…

Arun Aditya · 2026-05-06 20:35 · 0 claps · 3.2 min read
#aosp #selinux #android-framework #android-hal #android-binder
Open on Medium ↗
Wiki topics: 🔓 · Open Source

SELinux on Android: The “Secret Handshake” That Secures Your Service

If you’ve ever tried to add a custom daemon or a “Hello World” service to the Android /vendor partition, you’ve likely run into a brick wall called SELinux.

You write your C++ code, you define your init.rc file, you build the image, and... nothing. Your service won't start, or the logs are filled with "Permission Denied" errors.

The fix usually involves two weird-looking files: a .te (Type Enforcement) file and a file_contexts file. Today, let’s break down exactly how these two work together to get your service running.

The Big Picture: The “Security Sandbox”

Think of SELinux as a high-security office building.

  • The Binary: This is the person trying to get into a room.
  • The Domain: This is the specific “room” (sandbox) where the person is allowed to work.
  • The Label: This is the ID badge required to open the door.

In Android, every process must live in a “Domain.” If you don’t define one, the system won’t let your process do anything.

Step 1: The Blueprint (The .te file)

First, we have to tell the system that our service exists and what it is allowed to do. We use a Type Enforcement (.te) file for this.

# Define the "Room" (The Process Domain)
type helloworldd, domain;
# Define the "Key" (The Executable Type)
type helloworldd_exec, exec_type, vendor_file_type, file_type;
# Create the "Bridge"
init_daemon_domain(helloworldd)

What’s happening here?

  1. type helloworldd, domain;: We are creating a new sandbox called helloworldd.
  2. type helloworldd_exec...;: We are creating a special "Label" for our file. We are telling Android, "Any file with this label is a key that can unlock the helloworldd sandbox."
  3. init_daemon_domain(helloworldd): This is the Magic Macro. It tells the system: "When the init process sees a file labeled helloworldd_exec, it should automatically move that process into the helloworldd sandbox."

Step 2: The ID Badge (The file_contexts file)

Defining the rules in the .te file isn't enough. We actually have to "sticker" the physical file on the disk so the system recognizes it. This happens in the file_contexts file:

/vendor/bin/helloworldd    u:object_r:helloworldd_exec:s0

This line tells the Android build system: “When you create the image, look at the file located at /vendor/bin/helloworldd and give it the helloworldd_exec label."

Step 3: The “Secret Handshake” (How it works at boot)

When your Android device boots up, a “Handshake” occurs:

  1. The Trigger: init tries to run your binary at /vendor/bin/helloworldd.
  2. The Check: init looks at the file and sees the label helloworldd_exec.
  3. The Transition: init remembers the rule we wrote in Step 1. It says, "Aha! This file has the 'Key.' I will now start this process inside the 'helloworldd' sandbox."
  4. The Result: Your service is now running safely in its own restricted area, unable to mess with the rest of the system.

What happens if you skip these steps?

If you forget the Label, the system sees an “untrusted” binary trying to run. To protect the user, Android will block it immediately. You’ll see an AVC Denial in your logs that looks like a bunch of computer gibberish — this is just SELinux saying, “I don’t recognize this file’s ID badge, so it’s not coming in.”

Final Thoughts

SELinux isn’t just a hurdle; it’s the bodyguard of the Android OS. By using a Process Domain and a File Context label together, you are telling the system exactly who your service is and why it should be trusted.

Once you understand this “Secret Handshake,” you can move from just “making things work” to “making things secure.”

Bonus: How to Debug When Things Go Wrong

Even with the perfect label, your service might still crash or act up. When that happens, SELinux will leave a trail of “breadcrumbs” in your logs. Here is how to find them.

1. The Magic Command

To see only the SELinux-related errors, use this command in your terminal:

adb logcat | grep "avc: denied"

On some systems, you might need to use dmesg (the kernel log) instead:

adb shell dmesg | grep "denied"

2. Reading the “Gibberish”

A typical error (called an AVC Denial) looks like this: avc: denied { read } for pid=1234 comm="helloworldd" name="some_file" scontext=u:r:helloworldd:s0 tcontext=u:object_r:vendor_file:s0 tclass=file

Don’t let the long string scare you! Here is the cheat sheet to read it:

  • **{ read }**: The action your service tried to do.
  • **comm="helloworldd"**: The name of your service.
  • **scontext: The Source Context** (Who is trying to do it? Your service).
  • **tcontext: The Target Context** (What are they trying to touch? A file, a property, a socket).
  • **tclass**: The type of object being accessed (is it a file, dir, or unix_stream_socket?).

3. Pro-Tip: The “Cheat Code” (audit2allow)

If you have hundreds of denials, you don’t have to write every rule by hand. You can use a tool called audit2allow:

  1. Save your log to a file: adb logcat -d > logs.txt
  2. Run the tool: audit2allow -i logs.txt

It will literally spit out the exact allow rules you need to copy-paste into your .te file. Warning: Only use this as a hint—always double-check that you aren't giving your service too much power!


메타데이터
post_id
c70aa686e2c1
slug
selinux-on-android-the-secret-handshake-that-secures-your-service-c70aa686e2c1
url
https://medium.com/@aruncse2k20/selinux-on-android-the-secret-handshake-that-secures-your-service-c70aa686e2c1
canonical_url
https://medium.com/@aruncse2k20/selinux-on-android-the-secret-handshake-that-secures-your-service-c70aa686e2c1
author_url
https://medium.com/@aruncse2k20
status
ok
fetched_at
2026-06-27 23:56:40