ia: Benvenite! In mi blog io scribe in interlingua, italiano e anglese.

it: Benvenuti! Nel mio blog scrivo in interlingua, italiano e inglese.

en: Welcome! In my blog I write in Interlingua, Italian and English.

Debugging the “Factory mode” of BQ devices in Ubuntu Touch

As you know, I'm trying to get the FM radio to work in Ubuntu Touch, and I basically have it working on the Redmi Note 7 Pro. But then I remembered that the BQ Aquaris E4.5 (which is the first commercial device officially supporting Ubuntu Touch) also comes with an FM radio, so I decided to put some effort in getting that to work, too. You might think it's a waste of time, but as a matter of fact this device is built on a Mediatek SoC, and FM radio support is exposed to userspace in a very similar way across all Mediatek devices — so this work should be covering other devices as well.

It was relatively easy to get the FM radio to work on this phone: I can now tune to a frequency and see that the RDS data is received, but I cannot get any sound out of the speakers or headphones, which makes the whole radio experience a bit, uhm… suboptimal, let's say.

The problem is not new: back in the days when Ubuntu Touch was still supported by Canonical, sturmflut was trying to get the FM radio to work, and he met the same issues (see this and this messages in the mailing list). He also spent some time investigating how the Factory mode works (the FM radio is indeed one of the features that can be tested via the Factory mode, and it does work) and he mentioned that he could get gdb to attach to the factory mode program and could see the various ioctls being executed. Yesterday I tried to follow the same steps, but I failed quite soon: I simply could not connect with adb shell while the device was in factory mode, so no chances of debugging for me. ☹

Out of dispair, I tried just to manually run the program /system/bin/factory inside a Lomiri session and, to my surprise, it overlayed its yellow “Factory Mode” title on the screen — just to immediately quit afterwards. I tried running it with strace, and noticed these lines:

open("/sys/class/BOOT/BOOT/boot/boot_mode", O_RDWR|O_LARGEFILE) = 15
read(15, "0\n", 4)                      = 2
close(15)                               = 0
writev(5, [{"\3", 1}, {"FTM\0", 4}, {"[FTM UTILS] Unsupported factory "..., 39}], 3) = 44
writev(5, [{"\6", 1}, {"NVRAM\0", 6}, {"[MAIN] Unsupported Factory mode\n"..., 33}], 3) = 40

Of course, the /sys/class/BOOT/BOOT/boot/boot_mode file is read-only, so I couldn't just write 1 into it, but could a bind-mount work? Indeed it did! And after a few attempts, I verified that writing a value of 4 in the boot_mode file made the factory program happy. It was still unusable because it was acting as if the volume down button was being constantly pressed, so the cursor was always moving downwards, but killing the Lomiri session did the trick. In short, these are the steps you need to follow in order to run the Factory Mode from an ordinary boot session:

sudo -i  # become root
echo 4 > /tmp/trick
mount -o bind /tmp/trick /sys/class/BOOT/BOOT/boot/boot_mode
service lightdm stop  # wait a few seconds until this returns
unset LD_PRELOAD
/system/bin/factory

What is more surprising, is that the FM radio test is working even in this environment, and you can actually hear the sounds (for some reason, the FM radio item takes about 40 seconds to initialize when run under these conditions, but it eventually works). At this point, not only I could run gdb (which I didn't), but I could even run the factory program under strace and collect the logs.

At the moment of writing this post, I haven't yet examined the logs, so I'm not at all sure that they'll be enough to make audio work (I suspect that the factory mode binary might be playing some tricks that are somehow not replicable in a proper Linux system with Pulseaudio and lots of other services running), but I do have enough information to make at least a few attempts.

Stay tuned, and have a Merry Christmas!

Enabling the FM radio in Ubuntu Touch

I recently realized that my Xiaomi Redmi Note 7 Pro, on which I installed Ubuntu Touch not so long ago, has a working FM radio. One of the many psychological bugs of mine is the irrational urge I feel of having my hardware, no matter whether I use it or not, supported by Linux. So, the fact that I never listen to the radio is unfortunately not a reason to dissuade me from wasting time on getting the FM radio working in Ubuntu Touch.

This post is a quick summary of my investigation, which should serve as a note keeper for when I'll actually get to implement the feature.

The Android story

It was a bit surprising to find out that Android does not offer an API to use the FM radio, meaning that we cannot simply expose an API via libhybris. Every manufacturer is therefore choosing their own API, and Android applications are most often developed for a specific chipset family, or need to carry the code to support all the various devices.

For example, I found the RFM Radio Android application which supports a few Qualcomm Snapdragon processors, for which the FM radio functionality is exposed by a kernel driver via the V4L API. This means that the FM radio is probably working out of the box with QtMultimedia's QRadioTuner. Unfortunately the Note 7 Pro uses a newer Snapdragon, and even after some days of investigations I couldn't find out how the radio driver communicates to the userspace; but more on this below. Other chipsets offer other APIs, and I was glad to find that someone already wrote a Ubuntu Touch FM radio application for the Volla phone, which has a Mediatek board.

Anyway, the lack of a unified FM radio API is probably the reason why most of the so-called “FM radio” applications on Android are not really using the FM radio but rather streaming audio from the internet.

The FM radio in the Note 7 Pro

Before I start talking about a phone that no one cares about, let me say that what I'm going to write applies to several other Snapdragon chipsets and could be relevant to other phones. For one, the Redmi Note 9 Pro uses the very same bluetooth chipset as the Note 7 Pro (and in case you are wondering why I mentioned bluetooth, it's because the FM radio functionality is delivered by the same BT chip), so all what I'm going to write here is also relevant for that phone.

In order to figure out how the radio worked on this device, I took the drastic decision to reflash the stock Android (well, MIUI), started the preinstalled FM radio application, and meanwhile looked at the logcat messages (I'm not sure if this is needed, but before doing so I went to the “Developer options” in the system settings and set the debugging level to the maximum). Among a lot of noise, this showed me lines like these:

I android_hardware_fm: Opened fm_helium.so shared object library successfully
I android_hardware_fm: Obtaining handle: 'FM_HELIUM_LIB_INTERFACE' to the shared object library...
D FmReceiverJNI: init native called
D android_hardware_fm: BT soc is cherokee
I android_hardware_fm: Init native called
I android_hardware_fm: Initializing the FM HAL module & registering the JNI callback functions...
D radio_helium: ++hal_init
D fm_hci  : ++fm_hci_init
I fm_hci  : hci_initialize

Well, even without knowing nothing about all what these lines meant, I had something I could search the internet for. So I found the Qualcomm FM code in CodeAurora, and search for the code relative to my Snapdragon 675 (aka sm6150). I quickly gave up on trying to make some sense out of the git tag naming in that repository, and just tried to search for a tag which could be referring to my device. I found one, and started browsing its source tree.

It turns out that Qualcomm provides a Java package which applications can use, and which internally dlopen()s the fm_helium.so library, which in turn depends on the libfm_hci.so library. I had a quick look at the source code of these libraries, which are also present in the repository, but decided that I would have had more chances of success if I just tried to follow the JNI code, and in particular the android_hardware_fm.cpp file. I'm not sure why this code is not using the C structure types defined in the headers provided by the helium library, and instead redefines all the constants and accesses the character buffers by offsets — it might be just for historical reasons — but in any case I decided to follow along.

The fm-bridge program

Since we have a rather net separation between the Ubuntu Touch and the Android worlds (the Android services are running inside an LXC container, with all their Android libs and dependencies), one should not attempt to write an Ubuntu process that loads the Android libraries, because the libc used in Android is different, so things are likely not to work. But we can have Ubuntu and Android processes communicate over a socket or other kind of IPC; so, what I decided to go for, is writing a small C program that will live in the Android side, it will talk to the FM radio (via helium_fm), and accept commands / give replies via its stdin / stdout.

I unimaginatively called it “fm-bridge”, and you can look at its horrible code here. Really, I just said it was terrible, so why did you look at it? I definitely need to rewrite it from scratch, possibly using the helium headers, but as a proof of concept this also works. Then I carefully examined the logcat output while using the MIUI FM radio application in Android, and figured out what was the command sequence I had to input into fm-bridge's standard input in order to have it tune onto a given frequency. I'm publishing the commands here too, should I ever lose my notes:

enableSlimbus 1
setControl 0x8000004 1
enableSoftMute 1
setControl 0x8000029 0
setControl 0x800000c 1
setControl 0x800000d 1
setControl 0x800000e 1
setControl 0x800002b 0
setControl 0x8000007 4
setControl 0x8000006 0x40
setControl 0x8000006 0x40
setControl 0x8000011 0
setControl 0x800000f 1
getControl 0x8000010
setControl 0x8000010 0xef
setControl 0x800000f 1
setControl 0x800001b 1
setControl 0x8000012 0
setFreq 89300
setMonoStereo 1

I'm sure that not all of them are needed, but I'll figure out the optimal sequence later. In order to use this program on Ubuntu Touch, I had to alter the vendor partition to add this program, but also the fm_helium.so and libfm_hci.so libraries (more on that below).

When feeding the above commands to the fm-bridge in Ubuntu, I saw that I was getting a logcat output similar to the one from Android, which was mildly comforting. No sound was comint out of the speaker or out of the earplugs, but I was hardly expecting it all to work at the first try. And I got convinced that the FM tuner was indeed working, because typing the command “startSearch 1” made a new frequency appear in the logs, proving that the tuner had found another station and tuned onto it.

Getting the sound out

This was actually the easiest of the steps, thanks to the Ubuntu Touch FM radio application we have for the Volla: its source code mentions a few pulseaudio commands that worked perfectly in the Note 7 Pro too, despite the fact that the underlying chipset is totally different. This should not be as surprising as it might sound like, given that Android has a common audio API.

Just for my future reference, the commands are these:

pacmd set-source-port 1 input-fm_tuner
pactl load-module module-loopback source=1 sink=0

Ta-daaa! The radio was now playing from the phone loudspeakers! It was indeed quite loud, and the volume buttons did not seem to have any effect on it, but the volume can be controlled with pulseaudio:

pactl set-source-volume 1 50%

Of course, if we ever manage to make this into an Ubuntu Touch feature, we'll have to find a way to make the volume respond to the volume buttons.

Addind the needed files to the vendor partition

The simplest approach (and the one I took initially) is that of downloading the vendor.img into your PC, loop-mounting it, adding the fm_helium.so, libfm_hci.so and fm-bridge files to it and then umount the partition and reflash it (remembering to converting it from/to a sparse image before downloading/uploading it). This approach works flawlessly, but I'm wondering if one might incur into issues if the version of the NDK used to compile fm-bridge is different from the one that was used to compile the other vendor binaries, so I decided to give it a try to build the whole vendor partition myself.

This turned out to be a non trivial process, because I was using the Halium tree to build the vendor image, and not the LineageOS which was used to build the vendor image for my device: I could make an image, but it took some time before I figured out which were the needed packages that somehow got lost because of the Halium changes and that had to be added to the Makefile.

To help my weak memory, I expanded the README file in the violet port with the steps needed in order to build the vendor image.

A system service for the FM radio

While it could be possible for Ubuntu Touch applications to directly access the FM radio device in the same way that Android applications do, this is suboptimal for a few reasons. Even if we provided a shared library to deal with the various radio chipset implementations, the application would either need to be unconfined, or we'd had to provide an ever-changing AppArmor profile that peeks new holes every time that a new device implementation is added (and what if this implemenation uses a generic kernel device, which could be used for other goals too?) and in any case we'd have to make this policy restricted, since the RDS data provided by the radio stations would reveal the user location (well, the city at least) to the application. Not to talk about concurrent access to the radio device if two applications attempt to use it.

Therefore, my proposition (and what I'll implement, if I'll live long enough or if someone doesn't beat me to it) is to have a system service deal with the various hardware differences and expose a D-Bus API that will be hooked up as a QRadioTunerControl plugin, so that Qt applications will be able to just use the QtMultimedia APIs to access the radio.

The service would also need to talk to the trust-store, to let the user decide whether the application should really be granted access to the FM receiver (and when using the turst-store, this decision is remembered, and revocable from the System Settings's Security panel). Of course we'll also need to add a fm-radio AppArmor policy to let applications use this service.

QHtmlParser: writing an HTML parser with your brain switched off

While developing MiTubo I've recently felt the need of parsing HTML pages: the first problem I wanted to solve was implementing proper RSS feed detection when the user entered a website URL into MiTubo's search box, so that MiTubo would parse the site's HTML, look for <link rel="alternate"...> URLs in the HEAD section, and let the user subscribe to any video feeds found there.

A quick search in the internet did not provide a clear answer: I found a Qt HTML parser in (stalled) development, and a few other C++ or C parsers (among the latters, lexbor is the most inspiring), but all of them seem to take the approach of parsing the HTML file into a DOM tree, while I was hoping to find a lightweight SAX-like parser. Pretty much like Python's html.parser.

Anyway, I don't remember how it happened, but at a certain point I found myself looking at html.parser source code, and I was surprised to see how compact it was (apart, of course, for the long list of character references for the HTML entities!). Upon a closer look, it also appeared that the code was not making much use of Python's dynamic typing, so, I thought, maybe I could give it a try to rewrite that into a Qt class. And a few hours later QHtmlParser was born.

As this post's title suggests, the process of rewriting html.parser with Qt was quite straightforward, and the nice thing about it is that I didn't have to spend any time reading the HTML standard or trying to figure out how to implement the parser: I just had to translate Python code into C++ code, and thanks to the nice API of QString (which in many ways resembles Python's — or vice versa) this was not too hard. I even left most of the original code comments untouched, and reused quite a few tests from the test suite.

It was time well spent. :-)

If you think you might need an HTML parser for your Qt application, you are welcome to give it a try. It's not a library, just a set of files that you can import into your project; for the time being I only have a build file for QBS, but I'll happily accept contributions to make it easier to use QHtmlParser with projects built using other build systems. You can see here the changes I made in MiTubo to start using it and detect RSS feed in a webpage's HEAD.

That's all for now. And in case you missed the link before, you can find QHtmlParser here.