Android 10: Emulation of Magisk/SuperSU on an AOSP AVD

This article aims to:

  • describe the process for installing SuperSU or Magisk on an Android AOSP 10 Virtual Device (AVD) using the official Emulator, which is provided as part of the Android SDK tools
  • provide insight into the behaviour of SuperSU/Magisk su binaries vs AOSP's default su binary
  • highlight constraints that have been implemented for Android Builds starting from 9 and onwards, particularly in relation to partition layouts and the init process
  • highlight the limitations of working with/installing Magisk/SuperSU using Android’s SDK emulator

Limitations/Constraints

Android Studio SDK Emulator & AVD

  • The Android Virtual Device (AVD) that will used, runs using the Android SDK with an Atom x64 Image.
  • The Android Open Source Platform (AOSP) 10 image/AVD, is classified as a non-A/B device system.
  • From Android 9 onwards, system-as-root partitioning was introduced along with an ext4 deduped filesystem. For further details on these changes, refer to Appendix 1.
  • Emulation of physical device modes, such as recovery/fastboot/bootloader etc. are not supported.
  • AOSP 10 builds do not come bundled with Google Play Services/PlayStore. Google Inc. equivalents of these are available, but su mode on these devices is not enabled.

Limitations Applicable to Emulation of Magisk/SuperSU

I was unable to find references to emulated devices in the official installation guide for Magisk, as published by the developer, John Wu. All the installation methods outlined required some form of flashing, via either of the following recovery mode, custom recovery, boot image patching.

The final build and install of MagiskSU functions as expected. Installation of Magisk Manager modules is possible, however, the modules do not function as expected, and this is an area requiring further discovery. As such, addressing issues related to Magisk modules is out of scope.

For both SuperSU/Magisk, daemon processes will not persist after rebooting the AVD, i.e. you will need to manually restart backend daemons after rebooting the AVD. There are likely to be methods to launch the daemons via an init process, but this is not in scope for this article.

Pre-requisites for Magisk/SuperSU

If you’re familiar with system changes implemented in Android 9/10, Android SDKs and AVDs, then you can start following on from the next section and adapt instructions to suit your development environment.

If you are a beginner and have never worked with Android SDKs/AVDs or Android 9/10, then before proceeding, consider reading the Appendices that form part of this article.

Target Virtual Device Details

AVD Name: aosp10_sandbox
SDK Image/Platform: Intel x86 Atom System Image
ABI: x86_64
API Level: 29
Revision: 7

SELinux Policy

First things first, we are dealing with an SELinux (Security Enhanced Linux) system, which in context for this article, can succinctly be described by the following reference:

that the SELinux policy is in effect and things that it doesn’t want to allow won’t be allowed

  • To confirm that SELinux is enabled by default on the target AVD, start the device using command:
emulator -avd aosp10_sandbox
  • Run the following to see the current policy status:
adb root
adb shell getenforce
-->
Enforcing
  • To allow violations to be accepted (but still be logged), we would need to set the policy to permissive as follows :
adb shell "setenforce 0"
  • This would toggle the policy from Enforcing (1) to Permissive (0):
adb shell getenforce
-->
Permissive

Launch AVD with SELinux Policy as permissive by Default

SuperSU/MagiskSU will require the SELinux policy to be set to permissive.

  • For an AVD that has been created but has never been started/launched since creation, then start the AVD in permissive mode as follows:
emulator -avd aosp10_sandbox -selinux permissive
  • For an AVD that has already been created and launched without explicitly passing the -selinux permissive option, then the following can be used to restart the emulator in permissive mode:
emulator -avd aosp10_sandbox -selinux permissive -no-snapshot-load

Confirm AVD Details

Use adb to obtain/confirm some info about AVD/AOSP build.

  • Start the adb with root permissions by issuing:
adb root
  • Issuing an adb shell getprop lists all the properties for the Android AVD build. We will be building binaries later on, so we should confirm our target device's build architecture by running:
adb shell "getprop ro.product.cpu.abi"
--> x86_64
  • Confirm we are running Non-A/B system:
adb shell getprop ro.build.ab_update
--> false
  • Confirm Android release and SDK version:
adb shell getprop ro.system.build.version.release
--> 10

adb shell getprop ro.system.build.version.sdk
-->
29
  • Display location of existing su binary:
adb shell "which su"
--> /system/xbin/su
  • What else is at location /system/xbin/su?:
adb shell "ls -laR /system/xbin"
-->
-rwsr-x--- 1 root shell 11528 2019-08-02 15:35 su
  • This is the location where will later prepare for hosting SuperSU binaries/modules.

Installing SuperSU

The following instructions assume the AVD is in a “fresh” state, i.e, a “Wipe Data” has been performed and that the AVD’s SELinux policy is permissive.

Download files required for SuperSU version 2.82-SR5:

Install and Run SuperSU Daemon

  • Always ensure adb is running with root permissions when launching new host environment shells (such as Powershell/Command Prompt) :
adb root
  • Extract the folder x64 (for our build's architecture), from within SR5-SuperSU-v2.82-SR5-20171001224502.zip.
  • Since we’re running Android 10, we cannot mount/remount /system or / as writeable due to Android 10's system-as-root build properties and the ramdisk init process (refer to Appendix 1).
  • The root file system is mounted as ro - readonly
adb shell "cat /proc/mounts | grep -i ' / '"
-->
dev/block/dm-2 / ext4 ro,secla......
  • However, we have the ability create a read/write tmpfs filesystem, and mount this on /system/xbin
  • Create tmpfs filesystem 15MB size (enough space for the modules) with mount point /system/xbin
adb shell "mount -t tmpfs -o size=15M tmpfs /system/xbin"
  • Check mount:
adb shell "cat /proc/mounts | grep /system/xbin"
-->
tmpfs /system/xbin tmpfs rw,seclabel,relatime,size=15360k 0 0
  • Upload contents of folder x64\ (extracted from zip file mentioned previously) to /system/xbin. Assuming the contents were extracted to c:\temp\x64, and using Windows Powershell, we can upload as follows:
cd c:\temp\x64
adb push .\ "/system/xbin/"
-->
.\: 5 files pushed, 0 skipped. 85.6 MB/s (872152 bytes in 0.010s)
  • Check that files have been uploaded to correct target:
adb shell "ls -l /system/xbin"
-->
libsupol.so
su
suinit
sukernel
supolicy
  • Install SuperSU-v2.82-SR5.apk, as downloaded above. You can do this by dragging and dropping the apk from your local machine to the AVD, or run the following (assuming apk has been downloaded to c:\temp\SuperSU-v2.82-SR5.apk):
cd c:\temp
adb install SuperSU-v2.82-SR5.apk
-->
Performing Streamed Install
Success
  • Launch su in daemon mode:
adb shell "nohup /system/xbin/su 0 su --daemon &"
  • To confirm that the daemon is running on the AVD, run the following to search active processes:
adb shell "ps -ef | grep daemonsu"
-->
..
..daemonsu:mount:master
..daemonsu:master
..

Test SuperSU Works as Expected from within AVD

  • Run SuperSU app from within the AVD.
  • When you first launch SuperSU from within the AVD, ensure to respond to prompts as shown below.
  • The tested combination of the zip/apk referenced in the download requirements above, worked as expected.
  • Accepting requests from SuperSU to perform updates may introduce incompatibilities with the SU binary and SuperSU Apk.
Image for post
Image for post
  • To test if SuperSU correctly manages app requests for access, I downloaded one of my favourite File Explorers (X-plore) from Apk Mirror.
  • Follow this link and use the site’s search feature to find and download latest version of X-plore.
  • To install, use drag and drop from host to AVD, or run the following (I’ve saved the downloaded apk with name and location c:\temp\Xplore_4.20.04-42004.apk):
cd c:\temp
adb install Xplore_4.20.04-42004.apk
-->
Performing Streamed Install
Success
  • Go back into your AVD, locate X-plore and open the app.
  • Select Allow when prompted for permissions to access photos/media/files on the device.
  • Click the root (/) hierarchy from within X-plore to view the root file system (which requires su privileges).
  • You will be prompted by SuperSU to grant/deny su access for X-plore.
Image for post
Image for post

Installing Magisk

If you’ve gone through the SuperSU install, I suggest that you perform a “Wipe Data” against the AVD before proceeding.

The following sections relating to Magisk, were tested using build scripts from Magisk Git Repo.

Setup Python, JDK and Environment

  • According to the official repo, the build/install scripts require Python version 3.6+.
  • Install jdk8.
  • Ensure your JAVA_HOME environment variable is set correctly and is appended to you System Environment PATH to include Java bin directory. For example, on Windows, if your JDK installation path is C:\Java\jdk1.8.0_251, then set JAVA_HOME to this value and add %JAVA_HOME%\bin to your PATH.
  • Create a new ANDROID_HOME environment variable, which should be set to your Android SDK installation location. Refer to Appendix 3 for further details.
  • Python package colorama is required and can be installed using:
pip install colorama

Clone Git Repo Tree

  • Clone the official git repo, Magisk Git Repo (I’m performing the clone on my machine from directory c:\temp\repos):
cd C:\temp\repos
git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git
  • After the clone is done, local repo components will be under parent C:\temp\repos\Magisk.

Build Binaries

  • Edit repo file config.prop.sample and update the parameters for the lines shown below:
# The version name and version code of Magisk
version=20.4
versionCode=20400

# The version name and version code of Magisk Manager
appVersion=7.5.1
appVersionCode=267
  • Since we are building from source, the above versions/version codes can correspond to the latest version numbers listed at the repo's releases section. You can use your own custom numbers/codes, but the build script performs regex checks against these and expects parameter values to satisfy a certain format.
  • Save the file and rename it to config.prop.
  • Open a command/shell prompt at local repo’s root folder, containing build.py, and run the following commands sequentially:
python build.py ndk
python build.py all
  • Once the above commands have run to completion, the required binaries will be built and are located within the local repo hierarchy at location `C:\temp\repos\Magisk\native\out\{architecture}.
  • Apks are stored at C:\temp\repos\Magisk\out.

Magisk Binaries and SU Daemon

The script which performs the installation of binaries and initialises the MagiskSU daemon on the target AVD, is located within local repo at C:\temp\repos\Magisk\scripts\emulator.sh. This is a bash shell script.

To execute the script, follow the procedure below for your corresponding host OS.

Linux

  • The script can be executed directly from the host:
cd <repo_root_path>/scripts
chmod 755 emulator.sh
./emulator.sh
adb shell "mkdir -p /data/adb/magisk"
adb shell "cp /data/local/tmp/busybox /data/adb/magisk/"
adb push scripts/util_functions.sh /data/adb/magisk/

Windows

  • For installation from a Windows host (using Powershell), we first use adbto push the magiskinit64 binary, along other with other required scripts, to the target x86_64 AVD:
cd C:\temp\repos\Magisk
adb root
adb push native\out\x86\busybox scripts\emulator.sh /data/local/tmp
adb push native\out\x86\magiskinit64 /data/local/tmp/magiskinit
adb shell "mkdir -p /data/adb/magisk"
adb shell "cp /data/local/tmp/busybox /data/adb/magisk/"
adb push scripts\util_functions.sh /data/adb/magisk/
  • After the successful completion of the above, all necessary binaries (including installer emulator.sh) will have been pushed to target directory on AVD: /data/local/tmp.
  • Now run the following to execute emulator.sh on the target AVD:
adb shell sh /data/local/tmp/emulator.sh

Confirm MagiskSU Daemon Status

To confirm that MagiskSU is running on the target AVD, run:

adb shell "ps -ef | grep -i magisk"
-->
root ..... 00:00:00 magiskd

Install Magisk Manager APK to AVD

When we previously used build.py, we specified all as an input parameter. This not only generates output binaries required. The command also generates the necessary Magisk Manager APK at local repo location C:\temp\repos\Magisk\out. The Magisk Manager apk is located within this directory, and by default, is named app-debug.apk.

  • Install the apk using adb:
cd C:\temp\repos\Magisk\out
adb root
adb install app-debug.apk

-->
Performing Streamed Install
Success
  • Magisk Manager should now visible from within the AVD
Image for post
Image for post

Testing Magisk

Leveraging off the procedure used to test SuperSU, we can run a similar test for MagiskSU

  • Download X-plore from Apk Mirror.
  • To install, use drag and drop from host to AVD, or run the following (I’ve saved the downloaded apk with name and location c:\temp\Xplore_4.20.04-42004.apk):
cd c:\temp
adb install Xplore_4.20.04-42004.apk
-->
Performing Streamed Install
Success
  • Now go back into your AVD, locate X-plore and click to launch the app.
  • Select Allow when prompted for permissions to access photos/media/files on the device.
  • Click the root (/) hierarchy to view the root file system (which requires su privileges).
  • You will be prompted by MagiskSU to allow su access.
Image for post
Image for post

Final Comments

Hopefully you’ve found this article interesting and enjoyed the walk-thru.

As mentioned earlier, Magisk Manager modules install fine, but don’t function. As of now, more discovery is required to determine if there is a possibility of getting the modules working.

First thing I did after completing the setup, was downloading Google Play Store alternatives, F-Droid and Aptiode. You will also find that Apk Mirror is another excellent source for app downloads. The AVD serves as an excellent environment for trialling apps and gaining an understanding of Android.

Don’t forget that a reboot of the AVD will kill the su daemon, so you will need to manually start it again with the respective commands for SuperSU /MagiskSU.

Appendix 1: Android 10 Partitions/File System

When I first downloaded SDK platform images of Android 10 (API 29), I was keen to quickly get root (su) access and start remounting system mount points as writeable for both, Google Inc. and AOSP builds. The Google Inc. build was completely locked down with no su access and limited to read-only mounts. The AOSP build came with su access, but even as an su user, my attempts to override the mount behaviour for system/rootfs proved to be unsuccessful, including classic methods used in the past (adb shell, remount , .etc)

After some digging around, it became apparent that from Android 9 onwards, there were some architecture/design changes to the build. These changes included system-as-root partition layout, ramdisk/init process and implementation of a non-writeable filesystem (ext4 deduplicated). The filesystem change was broadcasted by the developer of Magisk via this tweet.

So, a brief overview describing the partition layout/init process for Android 10 follows.

From my interpretation of the official documentation, when Android 10 is compiled from source, it generates build artifacts into several locations. Amongst these are $TARGET_SYSTEM_OUT (aka system/), $TARGET_ROOT_OUT (aka / or ramdisk.img). These components are what make up the final system.img.

The below screenshot, taken from official Android documentation for Partition Layout for Non-A/B Devices shows the layout for system.img for pre and post system-as-root:

Image for post
Image for post

Android 10, Ramdisk init Stages

The following is a description Ramdisk stages, as taken from Android Official Documentation on Ramdisk.

In Android 10:

  • the first stage ramdisk contains the first stage init binary (which performs early mounting as specified by fstab entries) and vendor fstab files.
  • For devices with a boot-ramdisk (non-A/B), first stage init is a static executable located at /init. These devices mount system.img as /system, then perform a switch root operation to move the mount at /system to /. The contents of the ramdisk are freed after mounting has completed.
  • After first stage init finishes, it executes /system/bin/init with the selinux_setup argument to compile and load SELinux onto the system
  • Finally, init executes /system/bin/init again with the second_stage argument. At this point, the main phase of init runs and continues the boot process using the init.rc scripts.

Appendix 2: Download and Install Android Studio

If you do not have Studio installed then:

Image for post
Image for post
  • After Installation, launch Android Studio and when prompted choose the appropriate setting based on your requirements for studio settings
Image for post
Image for post
  • After this, you will be prompted with a “Welcome” and then onto installation type selection.
  • Select Custom
Image for post
Image for post
  • When prompted for default JDK location, leave the default setting (which uses the embedded JDK installed by Android Studio)
  • Select you UI Theme
  • When at the SDK Component Setup, go for the following options
Image for post
Image for post

and make a note of where the default SDK location path will be installed to, in my case running windows 10 %USERPROFILE%\AppData\Local\Android\Sdk is the default. Where %USERPROFILE on windows 10 is C:\Users\<username>.

https://developer.android.com/studio/command-line/variables

  • Choose defaults for HAXM
Image for post
Image for post
  • Keep selecting next, then verify settings and go on until you have finished the installation
  • After the Android Studio Initial Window Menu appears

Appendix 3: Configuring Android Studio/SDK Platform

Image for post
Image for post
  • Choose the Configure
  • Then SDK Manager and select config for SDK Platforms as follows
Image for post
Image for post
  • Once you press Apply
  • Now click on SDK Tools to configure
Image for post
Image for post
  • Click OK and when prompted with Licence Agreement, accept and continue
  • It will start download components. Wait until it’s done, then hit finish

AVD/SDK Environment Variables and Paths

Getting back to an earlier point regarding making a note of the location where we installed the sdk to…in my case it was %USERPROFILE%\AppData\Local\Android\Sdk which is the root path for SDK install for Windows 10.

Create a System Variable ANDROID_SDK_ROOT and set its value to %USERPROFILE%\AppData\Local\Android\Sdk.

To make sure our avdmanager, emulator, adb can be invoked with fully wqualifyed paths from command line, add the following entries to you System Path environment variable:

%ANDROID_SDK_ROOT%\emulator
%ANDROID_SDK_ROOT%\platform-tools
%ANDROID_SDK_ROOT%\tools\bin

AVD configuration and data files can be located at :

$USERPROFILE/.android/ 
$USERPROFILE/.android/avd/

Appendix 4: Create Target AOSP 10 Virtual Device

Back to our Android Studio Welcome menu window. To create an AVD named aosp10_sandbox:

  • click Configure gear
  • select AVD Manager
  • Click on Create Virtual Device
  • On the Virtual Device Configuration hardware config, select device that does not have the Play Store icon in the column
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post

Written by

Primarily a Learner/Coder with interests in Python, Cloud Technologies, Security and Automation. Pandas munching on Bamboo sticks give me the “Giggles” :))

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store