233 lines
6.7 KiB
Markdown
233 lines
6.7 KiB
Markdown
|
|
# Microdroid
|
||
|
|
|
||
|
|
Microdroid is a (very) lightweight version of Android that is intended to run on
|
||
|
|
on-device virtual machines. It is built from the same source code as the regular
|
||
|
|
Android, but it is much smaller; no system server, no HALs, no GUI, etc. It is
|
||
|
|
intended to host headless & native workloads only.
|
||
|
|
|
||
|
|
## Prerequisites
|
||
|
|
|
||
|
|
Any 64-bit target (either x86\_64 or arm64) is supported. 32-bit target is not
|
||
|
|
supported. Note that we currently don't support user builds; only userdebug
|
||
|
|
builds are supported.
|
||
|
|
|
||
|
|
The only remaining requirement is that `com.android.virt` APEX has to be
|
||
|
|
pre-installed. To do this, add the following line in your product makefile.
|
||
|
|
|
||
|
|
```make
|
||
|
|
$(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk)
|
||
|
|
```
|
||
|
|
|
||
|
|
Build the target after adding the line, and flash it. This step needs to be done
|
||
|
|
only once for the target.
|
||
|
|
|
||
|
|
If you are using `aosp_oriole` (Pixel 6) or `aosp_cf_x86_64_phone` (Cuttlefish),
|
||
|
|
adding above line is not necessary as it's already done.
|
||
|
|
|
||
|
|
## Building and installing microdroid
|
||
|
|
|
||
|
|
Microdroid is part of the `com.android.virt` APEX. To build it and install to
|
||
|
|
the device:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
banchan com.android.virt aosp_arm64
|
||
|
|
UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true m apps_only dist
|
||
|
|
adb install out/dist/com.android.virt.apex
|
||
|
|
adb reboot
|
||
|
|
```
|
||
|
|
|
||
|
|
If your target is x86\_64 (e.g. `aosp_cf_x86_64_phone`), replace `aosp_arm64`
|
||
|
|
with `aosp_x86_64`.
|
||
|
|
|
||
|
|
## Building an app
|
||
|
|
|
||
|
|
An app in microdroid is a shared library file embedded in an APK. The shared
|
||
|
|
library should have an entry point `AVmPayload_main` as shown below:
|
||
|
|
|
||
|
|
```C++
|
||
|
|
extern "C" int AVmPayload_main() {
|
||
|
|
printf("Hello Microdroid!\n");
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Then build it as a shared library:
|
||
|
|
|
||
|
|
```
|
||
|
|
cc_library_shared {
|
||
|
|
name: "MyMicrodroidApp",
|
||
|
|
srcs: ["**/*.cpp"],
|
||
|
|
sdk_version: "current",
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Then you need a configuration file in JSON format that defines what to load and
|
||
|
|
execute in microdroid. The name of the file can be anything and you may have
|
||
|
|
multiple configuration files if needed.
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"os": { "name": "microdroid" },
|
||
|
|
"task": {
|
||
|
|
"type": "microdroid_launcher",
|
||
|
|
"command": "MyMicrodroidApp.so"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
The value of `task.command` should match with the name of the shared library
|
||
|
|
defined above. If your app requires APEXes to be imported, you can declare the
|
||
|
|
list in `apexes` key like following.
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"os": ...,
|
||
|
|
"task": ...,
|
||
|
|
"apexes": [
|
||
|
|
{"name": "com.android.awesome_apex"}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Embed the shared library and the VM configuration file in an APK:
|
||
|
|
|
||
|
|
```
|
||
|
|
android_app {
|
||
|
|
name: "MyApp",
|
||
|
|
srcs: ["**/*.java"], // if there is any java code
|
||
|
|
jni_libs: ["MyMicrodroidApp"],
|
||
|
|
use_embedded_native_libs: true,
|
||
|
|
sdk_version: "current",
|
||
|
|
}
|
||
|
|
|
||
|
|
// The VM configuration file can be embedded by simply placing it at `./assets`
|
||
|
|
// directory.
|
||
|
|
```
|
||
|
|
|
||
|
|
Finally, you build the APK.
|
||
|
|
|
||
|
|
```sh
|
||
|
|
TARGET_BUILD_APPS=MyApp m apps_only dist
|
||
|
|
```
|
||
|
|
|
||
|
|
## Running the app on microdroid
|
||
|
|
|
||
|
|
First of all, install the APK to the target device.
|
||
|
|
|
||
|
|
```sh
|
||
|
|
adb install out/dist/MyApp.apk
|
||
|
|
```
|
||
|
|
|
||
|
|
`ALL_CAP`s below are placeholders. They need to be replaced with correct
|
||
|
|
values:
|
||
|
|
|
||
|
|
* `VM_CONFIG_FILE`: the name of the VM config file that you embedded in the APK.
|
||
|
|
(e.g. `vm_config.json`)
|
||
|
|
* `PACKAGE_NAME_OF_YOUR_APP`: package name of your app (e.g. `com.acme.app`).
|
||
|
|
* `PATH_TO_YOUR_APP`: path to the installed APK on the device. Can be obtained
|
||
|
|
via the following command.
|
||
|
|
```sh
|
||
|
|
adb shell pm path PACKAGE_NAME_OF_YOUR_APP
|
||
|
|
```
|
||
|
|
It shall report a cryptic path similar to `/data/app/~~OgZq==/com.acme.app-HudMahQ==/base.apk`.
|
||
|
|
|
||
|
|
Execute the following commands to launch a VM. The VM will boot to microdroid
|
||
|
|
and then automatically execute your app (the shared library
|
||
|
|
`MyMicrodroidApp.so`).
|
||
|
|
|
||
|
|
```sh
|
||
|
|
TEST_ROOT=/data/local/tmp/virt
|
||
|
|
adb shell /apex/com.android.virt/bin/vm run-app \
|
||
|
|
--log $TEST_ROOT/log.txt \
|
||
|
|
--console $TEST_ROOT/console.txt \
|
||
|
|
PATH_TO_YOUR_APP \
|
||
|
|
$TEST_ROOT/MyApp.apk.idsig \
|
||
|
|
$TEST_ROOT/instance.img \
|
||
|
|
--config-path assets/VM_CONFIG_FILE
|
||
|
|
```
|
||
|
|
|
||
|
|
The last command lets you know the CID assigned to the VM. The console output
|
||
|
|
from the VM is stored to `$TEST_ROOT/console.txt` and logcat is stored to
|
||
|
|
`$TEST_ROOT/log.txt` file for debugging purpose. If you omit `--log` or
|
||
|
|
`--console` option, they will be emitted to the current console.
|
||
|
|
|
||
|
|
Stopping the VM can be done as follows:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
adb shell /apex/com.android.virt/bin/vm stop $CID
|
||
|
|
```
|
||
|
|
|
||
|
|
, where `$CID` is the reported CID value. This works only when the `vm` was
|
||
|
|
invoked with the `--daemonize` flag. If the flag was not used, press Ctrl+C on
|
||
|
|
the console where the `vm run-app` command was invoked.
|
||
|
|
|
||
|
|
## Debuggable microdroid
|
||
|
|
|
||
|
|
### Debugging features
|
||
|
|
Microdroid supports following debugging features:
|
||
|
|
|
||
|
|
- VM log
|
||
|
|
- console output
|
||
|
|
- kernel output
|
||
|
|
- logcat output
|
||
|
|
- [ramdump](../docs/debug/ramdump.md)
|
||
|
|
- crashdump
|
||
|
|
- [adb](#adb)
|
||
|
|
- [gdb](#debugging-the-payload-on-microdroid)
|
||
|
|
|
||
|
|
### Enabling debugging features
|
||
|
|
There's two ways to enable the debugging features:
|
||
|
|
|
||
|
|
#### Option 1) Running microdroid on AVF debug policy configured device
|
||
|
|
|
||
|
|
microdroid can be started with debugging features by debug policies from the
|
||
|
|
host. Host bootloader may provide debug policies to host OS's device tree for
|
||
|
|
VMs. Host bootloader MUST NOT provide debug policies for locked devices for
|
||
|
|
security reasons.
|
||
|
|
|
||
|
|
For protected VM, such device tree will be available in microdroid. microdroid
|
||
|
|
can check which debuging features is enabled.
|
||
|
|
|
||
|
|
Here are list of device tree properties for debugging features.
|
||
|
|
|
||
|
|
- `/avf/guest/common/log`: `<1>` to enable kernel log and logcat. Ignored
|
||
|
|
otherwise.
|
||
|
|
- `/avf/guest/common/ramdump`: `<1>` to enable ramdump. Ignored otherwise.
|
||
|
|
- `/avf/guest/microdroid/adb`: `<1>` to enable `adb`. Ignored otherwise.
|
||
|
|
|
||
|
|
#### Option 2) Lauching microdroid with debug level.
|
||
|
|
|
||
|
|
microdroid can be started with debugging features. To do so, first, delete
|
||
|
|
`$TEST_ROOT/instance.img`; this is because changing debug settings requires a
|
||
|
|
new instance. Then add the `--debug=full` flag to the
|
||
|
|
`/apex/com.android.virt/bin/vm run-app` command. This will enable all debugging
|
||
|
|
features.
|
||
|
|
|
||
|
|
### ADB
|
||
|
|
|
||
|
|
If `adb` connection is enabled, launch following command.
|
||
|
|
|
||
|
|
```sh
|
||
|
|
vm_shell
|
||
|
|
```
|
||
|
|
|
||
|
|
Done. Now you are logged into Microdroid. Have fun!
|
||
|
|
|
||
|
|
Once you have an adb connection with `vm_shell`, `localhost:8000` will be the
|
||
|
|
serial of microdroid.
|
||
|
|
|
||
|
|
### Debugging the payload on microdroid
|
||
|
|
|
||
|
|
Like a normal adb device, you can debug native processes using `lldbclient.py`
|
||
|
|
script, either by running a new process, or attaching to an existing process.
|
||
|
|
Use `vm_shell` tool above, and then run `lldbclient.py`.
|
||
|
|
|
||
|
|
```sh
|
||
|
|
adb -s localhost:8000 shell 'mount -o remount,exec /data'
|
||
|
|
development/scripts/lldbclient.py -s localhost:8000 --chroot . --user '' \
|
||
|
|
(-p PID | -n NAME | -r ...)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Note:** We need to pass `--chroot .` to skip verifying device, because
|
||
|
|
microdroid doesn't match with the host's lunch target. We need to also pass
|
||
|
|
`--user ''` as there is no `su` binary in microdroid.
|