Skip to main content

Build & Deploy Android System Apps on Rooted Devices

Installing and running custom Android system apps unlocks powerful capabilities — including access to hidden or privileged Android APIs that are normally restricted from user-installed apps.

System apps can integrate more deeply with the OS, modify system behavior, and run with elevated permissions — enabling advanced use cases like process monitoring, bypassing app visibility restrictions, accessing protected directories, and hooking into low-level system services.

On rooted devices, this gives researchers, developers, and reverse engineers far more control over the Android runtime environment - potentially opening the door to make root detection easier to defeat.

Build a System App

  1. Use the following minimal-android-project repository as starting point in order to build a custom system app locally.

    git clone https://github.com/czak/minimal-android-project.git

    then:

    cd minimal-android-project
  2. Edit ./build.gradle to use this working example.

    Replace custom.package.name with the package name you want to app to have. This value needs to be consistent across the project.

    info

    Unless you know exactly what you're doing, nothing else should need to be changed here.

    buildscript {
    repositories {
    google()
    mavenCentral()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:8.5.2'
    }
    }

    apply plugin: 'com.android.application'

    android {
    namespace "custom.package.name"
    compileSdkVersion 34 // or 35 if you have Android 15 SDK

    defaultConfig {
    applicationId "custom.package.name"
    minSdkVersion 24 // Android 7.0+, safe minimum
    targetSdkVersion 34 // or 35 if using Android 15
    versionCode 1
    versionName "1.0"
    }

    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    }
    }

    repositories {
    google()
    mavenCentral()
    }
  3. Edit file at ./src/main/AndroidManifest.xml to look like the working example below. Replace package with the custom name of your system app package.

    note

    This example uses a .MainActivity, but this is optional — you can remove the <activity> block entirely if you don’t need a launcher or UI.

    Only do this if you know what you are doing.

    The line android:sharedUserId="android.uid.system" is what identifies the app as a system-level app:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:sharedUserId="android.uid.system">

    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />

    <application
    android:label="SystemApp"
    android:allowBackup="false">

    <activity android:name=".MainActivity" android:exported="true">
    <intent-filter>
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    </activity>

    </application>
    </manifest>
  4. Optional: If you choose to use a MainActivity, here's a working example. Ensure the package value is consistent with the value you chose.

    package com.corellium.mysystemapp;

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;

    public class MainActivity extends Activity
    {
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    TextView label = new TextView(this);
    label.setText("Hello world!");

    setContentView(label);
    }
    }
  5. Based on the package named assigned to the Android system app, directories need to be renamed to be consistent with the package naming.

    For example, if my system app is named com.corellium.mysystemapp then my project directories should look like this:

    ./src/main/java/com/corellium/mysystemapp
  6. Install Gradle. For MacOS, use brew:

    brew install gradle
  7. Kick off the build:

    gradle assembleRelease

A successful build places the release unsigned .apk in ./build/outputs/apk/release/minimal-android-project-release-unsigned.apk

For any build issues, review the output about why the build failed. If stuck, ensure you're using the example code as is and only changing the package name + relevant project directories.

Sign Your System App

  1. Obtain the necessary keys for signing. Android test keys have remained consistent across versions for AOSP-based builds.

    git clone https://android.googlesource.com/platform/build

    Change directories into build:

    cd build

    This is the key for signing:

    ./target/product/security/platform.pk8

    This is the certificate for signing:

    target/product/security/platform.x509.pem
  2. Sign the app using these keys from the repository cloned. We'll use apksigner to sign system app, the tool can be obtained by installing the Official Android Studio.

    Ensure you replace the example paths for key and cert to the path to the key and cert on your local system.

    apksigner sign --key /path/to/build/target/product/security/platform.pk8 --cert /path/to/build/target/product/security/platform.x509.pem --out customsystemapp.apk /path/to/app/unsigned/system/app                                                 

Install and Run Your System App

note

If the current steps are followed as is, the Android system should handle the correct file permissions for you.

  1. Run the adb connect command to connect to your Android device.

  2. Restart adb as root:

    adb root
  3. Remount root fs as read/write:

    adb shell mount -o remount,rw / 
  4. Create directory on the Android device. The directory name under /system/priv-app/ must match the APK name without the .apk extension.

    Example: If my .apk was named CorelliumSystemApp.apk, the directory name should reflect this.

    adb shell mkdir -p /system/priv-app/CorelliumSystemApp
  5. Push your system app to the Android device under the custom directory you created in the previous step.

    adb push custom_system_app.apk /system/priv-app/custom_system_app_dir/
  6. Grant privileged permissions to your system app. This is accomplished by creating an .xml file at /system/etc/permissions/ containing the following text. Ensure you replace the package with your custom package name.

    For best practice, name your .xml file using your app’s package name, like: privapp-permissions-com.corellium.mysystemapp.xml

    <permissions>
    <privapp-permissions package="com.corellium.mysystemapp">
    <permission name="android.permission.INSTALL_PACKAGES"/>
    </privapp-permissions>
    </permissions>
  7. Initiate soft reboot.

    adb reboot
  8. Once device boots back up, confirm the app is installed.

    adb shell pm path <package_name>

    If you included the .MainActivity in your system app you can run the app like:

    am start -n com.corellium.mysystemapp/.MainActivity

If you encounter issues installing/running your system app, try looking through logcat logs using the custom directory value you created under /system/priv-app/ when doing a grep:

adb logcat | grep "custom_system_app_dir"