Dummy Android System App
I ran the commands below from two locations: (1) the AOSP root folder and (2) the directory 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 elevated access, such as running ls /sys.
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Note: Declaring a system permission in a normal app does not grant that permission. Declaration and grant are different steps.
Android grants many privileged permissions only when the app is installed as a system app. 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 (Owner of RGR Innovate LLC)")
}
}
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 }
The issue still exists. I could not make it work, and I still could not test the expected privileged behavior even though the app is installed as a system app. I also tried the Cuttlefish emulator with the original boot.img, and saw the same result.
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.