Porting Guide

From AsteroidOS


Prerequisites

AsteroidOS is a Linux distribution, and therefore it can run on devices with mainline kernels, or with vendor kernels and hardware abstraction from other platforms such as Tizen. Most devices right now use Android kernels and Hybris, and therefore that's what this guide covers.

In order to run AsteroidOS using Hybris, your watch needs to meet the following criteria:

  • It needs to currently be running WearOS or other Android variant
  • It needs to have an unlockable bootloader, and you need a way to write boot images to the device.
    • This means you need a USB data connection to the watch. A significant number of watches do not expose USB data connections (either charging wirelessly, or charging via a connector that only supports USB power), but USB can be found on the inside by a determined hacker. Porting without USB is outside the scope of this guide.
  • You need to have a copy of the kernel source.
    • This kernel needs to include the DTS, a structure of files that describe the device's hardware - Most vendor kernels include the DTS, but on newer watches the DTS has often been found to be incomplete.

Goal

By the end, you should end up with an AsteroidOS hardware adaptation layer, which consists of:

  • OpenEmbedded recipes describing the compilation of the various HW adaptation components.
    • An appropriate vendor kernel, likely with some patches applied to fix compilation issues, or add functionality needed by Hybris and AsteroidOS.
    • A set of Wear OS’s proprietary binary blobs used through libhybris in order to access some of the Android’s HALs.
      • These may either be shipped as part of the AsteroidOS install, or be mounted from an existing partition on the watch
    • Some middleware configuration files to use your HW adaptation.

Preflight: AsteroidOS bitbake environment

  • If you've not got an AsteroidOS bitbake set up already, follow the instructions at Building AsteroidOS.
  • You may also want to go into meta-smartwatch and create a new branch for your port. As you're working, it's a good idea to make frequent commits so you can revert any changes.

Setting up the OpenEmbedded layer

In order to add the support of your watch to AsteroidOS, you’ll need to write an OpenEmbedded layer. An overview of layers is available in the OpenEmbedded page.

The easiest way to create an OpenEmbedded layer for your device is to copy and modify one that already exists within meta-smartwatch. The device you copy from will depend on the Android version of your target device.

Android version Best watch to copy
5 (lollipop) dory
6 (marshmallow) swift
7 (nougat)
8 (oreo) narwhal
9 (pie) catfish

Now, make sure you're in meta-smartwatch and run scripts/new-watch.sh SOURCECODENAME NEWCODENAME, such as scripts/new-watch.sh ray beluga , which will:

  • copy meta-BASECODENAME to meta-TARGETCODENAME
  • roughly replace all references to the base device's codename with the target device's codename

machine.conf

In your new meta-XXX layer, edit conf/machine/XXX.conf. This file describes general information about your specific watch, such as the kernel recipe, the display size and available sensors, and the packages that should be installed by default.

Linux Kernel recipe

The kernel recipe describes how to fetch/patch/configure/compile/install/package the vendor kernel. All of those steps may need to be modified from your base recipe.

Getting kernel source

First of all, you need to find the git repository, branch and commit containing the source code of the watch you want to work on. Replace SRC_URI and SRCREV accordingly.

Defconfig

Then, try to find your watch’s defconfig in the kernel’s arch/arm/configs/ directory.

Copy this base defconfig onto meta-XXX/recipes-kernel/linux/linux-XXX/defconfig.

If you're porting a watch with an android base before Oreo (8), you should be able to use the check-config script of meta-asteroid with the -w flag to fix a couple of configuration values.

Post Pie (Android 9), libraries generally need SELinux to function, and our config checker script hasn't yet been adapted for this. You'll need to go through and enable relevant SELinux options manually, and currently it's best to cross-reference against the changes that were made to the defconfig of beluga https://github.com/AsteroidOS/meta-smartwatch/blob/master/meta-beluga/recipes-kernel/linux/linux-beluga/defconfig or triggerfish https://github.com/AsteroidOS/meta-smartwatch/blob/master/meta-triggerfish/recipes-kernel/linux/linux-triggerfish/defconfig.

Extra-Note: The Yocto project provides a handy manual on how to work with the Linux kernel with OpenEmbedded.

Patches

If you already know you need to patch your kernel’s tree, add the path to the patches to SRC_URI.

Picking an Android boot image class

Kernel compilation will produce outputs (zImage-dtb, Image.gz-dtb, Image with separate dtb...) that will not directly boot on your watch. In order to be started by your watch’s bootloader, this image needs to be packed with an initramfs into a single android-format boot.img file. (for more information cf. Boot Process)

The kernel recipe for most watches inherits one of the AsteroidOS-specific boot image assembly classes, which means that compiling the kernel will also assemble a boot image.

Three different tools can be used to pack the boot.img file: abootimg, mkboot and mkbootimg. Since some watches only work with one of those tools (without any apparent reason), you can inherit one of the provided bbclass and adapt the config accordingly.

recipes-kernel/linux/linux-XXX-ver/img_info defines variables used by the image assembly process, such as the kernel command line.

Your first boot

You should now have done all the work necessary for the boot image to build - so this is a good time to try doing that.

Build

You can build just the bootable image for your watch using bitbake linux-XXX.

  • If the kernel compilation fails, you will need to patch the kernel. See if there are existing patches that fix the same problem for other watches.

Once the kernel/boot image build succeeds, it will produce an output at build/tmp/deploy/images/XXX/image.fastboot. The specific name of this bootable image will depend on the image format and codename of your device.

Forcing debug-ramdisk

At this stage, we don't want to worry about switching into the rootfs. To stop the boot process before this, modify the init script by adding a true after the line with the debug-ramdisk check. This will cause the watch to start adb instead, which serves as a useful checkpoint.

Boot

To get your new image booted on the watch, get it into fastboot and make sure you've already unlocked the bootloader. Then you'll need to do one of the following things, depending on your device:

  • run fastboot boot image.fastboot. This tends not to work on newer watches
  • flash the image to recovery partition using fastboot flash recovery then
    • fastboot reboot recovery (which doesn't work the same on all devices)
    • use a button combination to reboot to recovery

The following should now happen

  • Fastboot will load the DTB and your kernel
  • Once the kernel boots, it will load the initramfs' init script
  • In a normal boot, the init script will try to mount the rootfs, chroot into it and start systemd (see Boot Process)
  • However, if you forced debug-ramdisk (as mentioned above), it will instead start the adb daemon. You should now be able to see the watch appear as a usb adb device

Fastboot should load your kernel which will load the initramfs’s init script which will eventually load you rootfs’s init system. (for more information cf.) You can usually know if those steps have successfully been completed when the watch stays up and running and shows the AsteroidOS boot logo for a while (shown by psplash which is ran by systemd on the rootfs). In case your boot stops earlier, you have a boot problem to debug.

You need to proceed by elimination to find what causes your boot to fail. First of all, disable the ramfs → rootfs switch by adding a bash infinite loop early in your ramdisk’s init script. If you notice your watch does not crash anymore, you may have an issue with your rootfs. The Troubleshooting section of the Boot Process page should help you retrieve your systemd logs and isolate the cause of your boot problem.

If your watch keeps on crashing, you may have a problem with your kernel. Start by making sure you tried the different boot.img packing tools. If the problem persists, you’ll want to retrieve your kernel’s message logs. This can usually be retrieved from a /proc/last_kmsg virtual file in Wear OS when enabling the CONFIG_ANDROID_RAM_CONSOLE kernel option. Analyzing the logs should also help narrowing down the issue to a faulty driver or config.

Android binary blobs

Libhybris provides adaptations between Android HAL (hardware abstraction layers) and Linux-oriented userspace APIs such as Qt QPA for display, Geoclue for GPS, SensorFW for sensors. For this to work, we need to provide Hybris with some compiled bits of android: one group of binaries is patched for hybris and compiled from android source, while the other group of android binaries is extracted from the retail firmware of your device.

For Android <=7: The android-xxx recipe

You'll be doing all your work in the meta-XXX/recipes-android/android/android_XXX-*.bb file. Here, in SRC-URI, you'll need two tarballs. These provide the two groups of android binaries needed by hybris:

For Android >=8: Mounting /vendor/

On newer versions of android, we instead mount the /vendor/ partition as it already exists on the device.

  • in machine.conf, include on of the generic oreo or pie android recipes.
    • If there's no recipe for your android version, you may be able to get away with an earlier version, such as aurora using the android 9 blobs recipe for an android 11 port
    • If there's no recipe for your device architecture, you will need to build a new set of binaries for your device - ask in the matrix chat for help, as this process isn't well-documented.

USB

If you have your AsteroidOS system running, you will want to get a shell access to it with adb or SSH over USB. Use dmesg -w on your computer to check if your watch appears after having been booted for a few seconds. If you don’t see your watch, you may have a problem with your USB setup.

First of all, try to replicate the config of your Wear OS’s init scripts in your ramdisk’s init script. Once you’re sure the USB config is the same, you should try to run adbd from your ramdisk instead of the rootfs, if it solves the problem you may have an issue with usb_moded not starting adbd or RNDIS properly.

If the problem is the same, you may have a problem with your kernel’s config or drivers. First of all, make sure CONFIG_USB_G_ANDROID is still enabled in the generated build/tmp/work/XXX-oe-linux-gnueabi/linux-XXX/*/git/.config as it is quite common to find bitbake removing this option. If the option is still there, your kernel may need special patches.

Display

As soon as you’ll get access to a shell on AsteroidOS, you may find yourself without asteroid-launcher running. Checking the system logs with journalctl –no-pager or systemctl –no-pager usually shows a segfault from the asteroid-launcher process. This is to be expected on your first run and there is nothing to worry about. In order to reproduce the bug, you can start asteroid-launcher manually with:

EGL_PLATFORM=hwcomposer QT_QPA_PLATFORM=hwcomposer asteroid-launcher

asteroid-launcher uses lipstick which uses the hwcomposer QPA which uses libhybris which uses the Android graphic HAL to display things on screen. You may need to adapt the Android graphic HAL to answer the needs of hybris. (usually a matter of starting the right android boot services, having the right files placed in the right directories, having the right system.prop options etc…)

For more information on the Graphic Stack you may want to refer to the associated documentation page. For more information on how to debug an issue, check the Troubleshooting paragraph of this page.


Touch

Once you’ll get asteroid-launcher displaying things on screen, you may have no touch feedback from the UI. If that is the case, you’ll need to modify the asteroid-launcher start command to use a different evdevtouch file. For example, tetra uses this bbappend to use /dev/input/event1 instead of the default event0.

Audio

Smartwatches usually don’t carry speakers but they often have microphones. AsteroidOS does not use those microphones yet but it’s usually easy to support and should be supported for the future of the project. AsteroidOS uses pulseaudio for its audio stack. pulseaudio takes benefits from the pulseaudio-droid-card module to use the Android’s Audio HAL. In order to test the microphone, you can install and run parec from the build/tmp/work/armv7vehf-neon-oe-linux-gnueabi/pulseaudio/15.0-r0/packages-split/pulseaudio-misc/ package. If you lack an audio interface, make sure you loaded the audio firmware.

In case a problem persist, enabling extra debug information from pulseaudio can give you detailed info on the cause of the issue.

To play an audio file use the following command:

gst-launch-1.0 filesrc location=input.mp3 ! mpegaudioparse ! mpg123audiodec ! pulsesink

To make an audio recording, use the following command:

gst-launch-1.0 pulsesrc ! audioconvert ! wavenc ! filesink location=recording.wav

Bluetooth

Smartwatches make extensive use of Bluetooth for connectivity. The AsteroidOS project comes with a set of Bluetooth Low Energy profiles implemented in the asteroid-btsyncd server daemon and AsteroidOSSync client. (for more information cf. Bluetooth) Bluetooth required a few extra steps to work on your port.

asteroid-btsyncd uses BlueZ’s DBus API to create a BLE server. Those APIs require a recent Bluetooth subsystem in your kernel. Unfortunately, Wear OS watches usually come with outdated kernels and a newer BT subsystem needs to be backported. The patch used in dory can be found here and should be adaptable to your kernel’s tree with very few conflicts.

Most Wear OS smartwatches carry a Broadcom BT chip that requires a proprietary firmware to be applied with a tool named brcm-patchram-plus. This tool is called from a systemd service named patchram.service, your HW adaptation layer should include a modified patchram.service file that uses the provided vendor’s firmware and tty line. Information about the config you should use can be found in the Android’s libbt-broadcom repository.

With a backported BT subsystem and patched BT firmware, you should already be able to get Bluetooth connectivity on your port but with very limited battery life. Broadcom’s bluetooth’s low power mode is handled by a kernel module named Bluesleep (or more recently Nitrous) that expects custom information to be written in /proc/lpm/. With BlueZ, this proc interface is never used hence the kernel driver must be adapted to receive HCI events from the BT subsystem with a patch. Finally, brcm-patchram-plus must also be compiled with a special flag defining the target device and the low power mode options to use.


Sensors

In AsteroidOS, sensors can be used by developers through the QtSensors API that uses the sensorfw backend. Sensorfwd is a sensor daemon that can use libhybris to interface with the Android’s Sensors HAL.

Most watches have a dedicated microcontroller that interfaces with sensors. This means that a daemon or kernel module (or something else) is responsible to upload and interface with the microcontroller from AsteroidOS.

In order to use sensors on AsteroidOS, you need to make sure libhybris recognizes your sensors by running the test_sensors command. For some platforms it is required to start a sensor daemon. This can usually be found in Wear OS's init scripts. A likely output of test_sensor for a platform needing a sensor daemon is:

sensors_open() failed: Operation not permitted

For more information on how to debug an issue, check the Troubleshooting paragraph of this page.


Haptics

In AsteroidOS, vibrations can be used by developers through the QtFeedback and ngfd APIs that usually rely on a libhybris backend which uses the Android’s vibration HAL. In order to use vibration on Asteroid with the Android HAL, you need to make sure libhybris recognizes your vibrator by running the test_vibrator command. For more information on how to debug an issue, check the Troubleshooting paragraph of this page.

A much cleaner way to vibrate in AsteroidOS would be to use an ffmemless kernel driver and the sensorfwd’s ffmemless backend but most watches don’t come with such a kernel driver and it would need to be developed.


WLAN

WLAN is not used by AsteroidOS’s user-interface yet but it should be supported for the future of the project. Some information about setting up a Wi-Fi interface can be found in the last paragraph of the SSH documentation page.

Troubleshooting

While porting AsteroidOS to your watch, you will most likely meet all kind of problems. In order to debug what’s going wrong with your setup you should install debug tools such as strace and gdb and the required debug sybols to your generated rootfs. For example, you can temporarily append the strace gdb asteroid-launcher-dbg packages to the asteroid-image class.

Other logical places to look for information that may help you solve the problems are: dmesg, journalctl --no-pager and /system/bin/logcat.

Once you come up with more information about your bug, your first reflex should be to search the asteroid’s IRC logs for similar issues. The SailfishOS porters community is another great place to search for information, you may also want to try your chance at searching the merproject’s IRC logs. Lastly you can also look through the GitHub issues for more information by other porters.

If you still couldn’t find a solution to your problem in the logs, don’t hesitate to ask questions on the #asteroid IRC or #Asteroid:matrix.org channel. There should be several porters able to help you.

Upstream your work

That’s it, you can finally use AsteroidOS on your watch! Sharing your work with the rest community should be the final step to ensure your layer will be maintained through time. Start by requesting on IRC the creation of a new AsteroidOS/meta-XXX-hybris repository and send a pull request to this repository. Your work will be carefully reviewed and commented before being integrated upstream. You’ll also need to send a pull-request to the AsteroidOS/asteroid repository to integrate your machine to prepare-build.sh and join the very privileged circle of AsteroidOS porters! Make sure your work is correctly detailed on the Porting Status page and share a photo of your watch for public twitter announcement.