Dummy Android System App

I am running all the below commands from two locations: 1) from aosp root folder 2) from where the cuttlefish android emulator is installed.

upgautam@amd:/opt/aosp$ source build/envsetup.sh
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined$ HOME=$PWD ./bin/launch_cvd

upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb devices
List of devices attached
0.0.0.0:6520	device

We built a dummy system app, and tried to test its superpower, such as ls /sys folder.

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

Note: Declaraing the same system permission for normal app doesn't grant the permission as permission declaration is different to permission granting.

Android system ensures if those system permission are declared and those are the system app then they are granted those permission. To test this, I modified my dummy app as follows,

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.DummySystemApplication"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.DummySystemApplication">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

And, my activtiy as,

package com.example.dummysystemapplication

import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.dummysystemapplication.ui.theme.dummysystemapplicationTheme
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.OutputStreamWriter
import kotlin.concurrent.thread


class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        disableSELinux()
        executeRootCommand()

        setContent {
            dummysystemapplicationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = "Uddha P. Gautam",
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }

    private fun disableSELinux() {
        thread {
            try {
                // Start the root shell with /system/xbin/su 0
                val command = "/system/xbin/su 0"
                val process = Runtime.getRuntime().exec(command)

                // Send setenforce 0 command to the root shell
                val outputStream = process.outputStream
                val writer = BufferedWriter(OutputStreamWriter(outputStream))
                writer.write("setenforce 0\n")
                writer.flush()

                // Capture the output of the command
                val output = process.inputStream.bufferedReader().use(BufferedReader::readText)
                process.waitFor()

                runOnUiThread {
                    Toast.makeText(this, "SELinux disabled:\n$output", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                e.printStackTrace()
                runOnUiThread {
                    Toast.makeText(this, "Error disabling SELinux", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }


    // Function to execute root commands
    private fun executeRootCommand() {
        thread {
            try {
                // Start the root shell
                val command = "/system/xbin/su 0"
                val process = Runtime.getRuntime().exec(command)

                // Send the "ls /sys" command to the root shell
                val outputStream = process.outputStream
                val writer = BufferedWriter(OutputStreamWriter(outputStream))
                writer.write("ls /sys\n")
                writer.flush()

                // Capture the output of the command
                val output = process.inputStream.bufferedReader().use(BufferedReader::readText)
                process.waitFor()

                runOnUiThread {
                    Toast.makeText(this, "Root Command Output:\n$output", Toast.LENGTH_LONG).show()
                }
            } catch (e: Exception) {
                e.printStackTrace()
                runOnUiThread {
                    Toast.makeText(this, "Can't run root command", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    dummysystemapplicationTheme {
        Greeting("Uddhav P. Gautam")
    }
}

Build the apk and push. But this time, I also installed magisk app and from there patched the boot.img, I pulled that patched boot.img to host and then stopped and started cuttlefish emulator with new patched boot.img.

upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb push ~/Desktop/DummyApp/app-debug.apk /system/priv-app/dummy_app/app-debug.apk
adb: error: failed to copy '/home/upgautam/Desktop/DummyApp/app-debug.apk' to '/system/priv-app/dummy_app/app-debug.apk': remote secure_mkdirs() failed: Read-only file system
/home/upgautam/Desktop/DummyApp/app-debug.apk: 1 file pushed, 0 skipped. 674.3 MB/s (8670593 bytes in 0.012s)

To fix this read-only error,

upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb reboot
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb root
restarting adbd as root
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb shell
vsoc_x86_64:/ # whoami
root
vsoc_x86_64:/ # setenforce 0
vsoc_x86_64:/ # disable-verity
AVB verification is disabled, disabling verity state may have no effect
enabling overlayfs
Reboot the device for new settings to take effect
vsoc_x86_64:/ # reboot
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb root
adb: unable to connect for root: no devices/emulators found
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb root
restarting adbd as root
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb shell
vsoc_x86_64:/ # mount -o rw,remount /system
vsoc_x86_64:/ # mkdir /system/priv-app/dummy_app/
vsoc_x86_64:/ # exit
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb push ~/Desktop/DummyApp/app-debug.apk /system/priv-app/dummy_app/app-debug.apk
/home/upgautam/Desktop/DummyApp/app-debug.apk: 1 file pushed, 0 skipped. 21.7 MB/s (8670593 bytes in 0.380s)
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb reboot
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb root
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb shell
vsoc_x86_64:/ # am start com.example.dummysystemapplication/.MainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.dummysystemapplication/.MainActivity }

Still the issue exists. I can't make it work. I can't test its superpower, although it is a system app. I tried cuttlefish emulator with original boot.img, the same issue.

upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb push ~/Desktop/DummyApp/app-debug.apk /system/priv-app/dummy_app/app-debug.apk
/home/upgautam/Desktop/DummyApp/app-debug.apk: 1 file pushed, 0 skipped. 22.6 MB/s (8670593 bytes in 0.366s)
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb root
adbd is already running as root
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb shell
vsoc_x86_64:/ # chmod 644 /system/priv-app/dummy_app/app-debug.apk
vsoc_x86_64:/ # chown root:system /system/priv-app/dummy_app/app-debug.apk
vsoc_x86_64:/ # reboot   //when it restarts the apk will be installed as system app
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb root
restarting adbd as root
upgautam@amd:/opt/cuttlefish/cuttlefish-run/combined/bin$ ./adb shell
vsoc_x86_64:/ # setenforce 0
vsoc_x86_64:/ # am start com.example.dummysystemapplication/.MainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.dummysystemapplication/.MainActivity }
#Still I get Error.