Posted
over 1 year
ago
by
Alberto Mardegan
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)
... [More]
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! [Less]
|
Posted
over 1 year
ago
by
Alberto Mardegan
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)
... [More]
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!0 0 [Less]
|
Posted
over 1 year
ago
by
Alberto Mardegan
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)
... [More]
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!
0 0 [Less]
|
Posted
over 1 year
ago
by
Philip Van Hoof
We are a few years further. A few years in which we all tried to make a difference.
I’m incredibly proud of my achievement of QTBUG-61928. At the time I thought I could never convince the Qt development team of changing their APIs. They did and
... [More]
today in Qt6 it’s all very much part of the package.
I want to thank Thiago and others. But I also think it’s a team effort. It might not be because of just me. But I still feel a little bit proud of having pushed this team just enough to make the changes.
I am now at a new Qt bug report. This time it’s about int64_t. I think that QModelIndex should be completely supporting it. Again, I think a lot. And I have a lot of opinions. But I anyway filed QTBUG-99312 for this.
0 0 [Less]
|
Posted
over 1 year
ago
by
Alberto Mardegan
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
... [More]
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. [Less]
|
Posted
over 1 year
ago
by
Alberto Mardegan
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
... [More]
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.
0 0 [Less]
|
Posted
over 1 year
ago
by
Alberto Mardegan
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
... [More]
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.0 0 [Less]
|
Posted
over 1 year
ago
by
Pavel Rojtberg
This post will be for those of you that care about privacy – i.e. if you want that information about you is exclusively under your control.In that context not only Google is to blame, but actually most of the cloud services we know and use today.
... [More]
Still Google will serve us as a nice placeholder as it is the market-leader when it comes to providing free services in exchange for your user-profile that Google in turn uses to sell target advertising. Even if you are fine with that, Google is also infamous for killing services – which might hit the one you rely on eventually.
As the world is moving mobile-first, a prerequisite for replacing a service is that we can easily integrate the replacement with an Android device.Some might wonder why I chose Android here, given that it is made by Google. See, the problem is not who makes a service/ device, but who controls it. And with Android the main leverage for Google is bundling its services. If you take them away, the device itself is fully under your control – in contrast to Apple/ iOS.
Now, that we set the stage, lets start with:
Nextcloud
At the heart of our efforts will be Nextcloud. This started as an open-source alternative to Dropbox/ Google Drive, but is nowadays grown into a platform for a plethora of different services.The main selling-point is that you can just install Nextcloud on your own machine – ensuring that your data stays private. The software being Open-Source also means that it is not under control of a single corporation – in fact Nextcloud was forked from its predecessor Owncloud after a corporation tried to put the screws on its users.
Note pharmrx.online , that if you are serious about this, you will need to invest around 500€ to get a machine for hosting that is decently fail-safe. If you rather want to be cheap, you can also just use a RaspberryPi to get away with less than half of that amount. For inspiration, you can take a look on the built I use or on the list of commercial nextcloud device providers.
Files & Photos
By using the Nextcloud Android App, you can directly replace Google Drive/ Dropbox as this is the core functionality of Nextcloud.
Additionally, the App allows you to automatically back-up your Photos/ Videos and free the local storage so you can stop using Google Photos too.
Contacts & Calendar
Nextcloud also supports Contacts and Calendar out of the box. To integrate them with into your Android Device there is the DAVx5 app. This app will function as an additional data provider, so you can just continue using the stock Google Contacts and Calendar apps. Those will, however, stop sending any data to Google.
This is especially useful, if you run a small-business and must ensure that your customer data is private according to the CCPA/ GDPR.
Nextcloud News
An often overlooked part of your privacy is Google News (also part of Google Now). Each time you view an article there, Google can mine your interests and political views – similar to what Facebook does. And by now you should know where these things can lead.
Another drawback here is that the Google Algorithm will create a bubble for you by only showing content coherent with your current world-view.I still prefer to manually choose the news sources – to create that bubble myself.
To do that, one usual subscribes some Web-feeds using a Feed aggregator like the Feedly service – similar to what Google Reader used to offer, before being killed by Google.
The go-to app here is Nextcloud News combined with the Android App for a pleasant mobile usage.
Music
Most of you are probably just stream music via Spotify/ Youtube Music, but keep in mind that these services merely rent the songs and as such they can arbitrarily disappear from your library.
Therefore, I like to have my own copy of the song. Unfortunately it is very inconvenient to juggle mp3s around for getting the music to various devices.
Google Play Music used to offer best of both worlds for me, where you could upload your own music files and manage your playlists in one place. Additionally, you could make the music available offline by pinning individual playlists on your device.Unfortunately, that concept did not allow Google to arbitrarily inject ads into my music stream and therefore the service got killed as well.
Nextcloud Music to the rescue! This app picks up your music library via Nextcloud files and allows to stream that via the Browser or the Subsonic API.This is where the DSub Android Player takes over. As with Play Music, you can either stream the library or pin individual songs/ playlists for offline use. Note: untick the “Authorization Basic headers” box when setting-up Nextcloud Music.
Other
If you clicked on the links above, you probably noticed the F-Droid alternative app store for Android.Getting your apps there ensures that you are using verified packages and open-source software. You can easily use it alongside with the Play Store. If that is too inconvenient for you, all of the above apps are also available via the Play Store.
Finally, there is the web-browser. If you do not log in with your Google Account, using Chrome is mostly safe. However, I suggest switching to Firefox. See the my original article on that topic for details. In short; the main reason is the availability of extensions. Those allow you to block ads on the mobile web too and use Youtube in the backround.
Header Image: Digital Chains by stanjourdan (CC-BY-SA-2.0)
0 0 [Less]
|
Posted
over 1 year
ago
by
Pavel Rojtberg
This post will be for those of you that care about privacy – i.e. if you want that information about you is exclusively under your control.In that context not only Google is to blame, but actually most of the cloud services we know and use today.
... [More]
Still Google will serve us as a nice placeholder as it is the market-leader when it comes to providing free services in exchange for your user-profile that Google in turn uses to sell target advertising. Even if you are fine with that, Google is also infamous for killing services – which might hit the one you rely on eventually.
As the world is moving mobile-first, a prerequisite for replacing a service is that we can easily integrate the replacement with an Android device.Some might wonder why I chose Android here, given that it is made by Google. See, the problem is not who makes a service/ device, but who controls it. And with Android the main leverage for Google is bundling its services. If you take them away, the device itself is fully under your control – in contrast to Apple/ iOS.
Now, that we set the stage, lets start with:
Nextcloud
At the heart of our efforts will be Nextcloud. This started as an open-source alternative to Dropbox/ Google Drive, but is nowadays grown into a platform for a plethora of different services.The main selling-point is that you can just install Nextcloud on your own machine – ensuring that your data stays private. The software being Open-Source also means that it is not under control of a single corporation – in fact Nextcloud was forked from its predecessor Owncloud after a corporation tried to put the screws on its users.
Note, that if you are serious about this, you will need to invest around 500€ to get a machine for hosting that is decently fail-safe. If you rather want to be cheap, you can also just use a RaspberryPi to get away with less than half of that amount. For inspiration, you can take a look on the built I use or on the list of commercial nextcloud device providers.
Files & Photos
By using the Nextcloud Android App, you can directly replace Google Drive/ Dropbox as this is the core functionality of Nextcloud.
Additionally, the App allows you to automatically back-up your Photos/ Videos and free the local storage so you can stop using Google Photos too.
Contacts & Calendar
Nextcloud also supports Contacts and Calendar out of the box. To integrate them with into your Android Device there is the DAVx5 app. This app will function as an additional data provider, so you can just continue using the stock Google Contacts and Calendar apps. Those will, however, stop sending any data to Google.
This is especially useful, if you run a small-business and must ensure that your customer data is private according to the CCPA/ GDPR.
Nextcloud News
An often overlooked part of your privacy is Google News (also part of Google Now). Each time you view an article there, Google can mine your interests and political views – similar to what Facebook does. And by now you should know where these things can lead.
Another drawback here is that the Google Algorithm will create a bubble for you by only showing content coherent with your current world-view.I still prefer to manually choose the news sources – to create that bubble myself.
To do that, one usual subscribes some Web-feeds using a Feed aggregator like the Feedly service – similar to what Google Reader used to offer, before being killed by Google.
The go-to app here is Nextcloud News combined with the Android App for a pleasant mobile usage.
Music
Most of you are probably just stream music via Spotify/ Youtube Music, but keep in mind that these services merely rent the songs and as such they can arbitrarily disappear from your library.
Therefore, I like to have my own copy of the song. Unfortunately it is very inconvenient to juggle mp3s around for getting the music to various devices.
Google Play Music used to offer best of both worlds for me, where you could upload your own music files and manage your playlists in one place. Additionally, you could make the music available offline by pinning individual playlists on your device.Unfortunately, that concept did not allow Google to arbitrarily inject ads into my music stream and therefore the service got killed as well.
Nextcloud Music to the rescue! This app picks up your music library via Nextcloud files and allows to stream that via the Browser or the Subsonic API.This is where the DSub Android Player takes over. As with Play Music, you can either stream the library or pin individual songs/ playlists for offline use. Note: untick the “Authorization Basic headers” box when setting-up Nextcloud Music.
Other
If you clicked on the links above, you probably noticed the F-Droid alternative app store for Android.Getting your apps there ensures that you are using verified packages and open-source software. You can easily use it alongside with the Play Store. If that is too inconvenient for you, all of the above apps are also available via the Play Store.
Finally, there is the web-browser. If you do not log in with your Google Account, using Chrome is mostly safe. However, I suggest switching to Firefox. See the my original article on that topic for details. In short; the main reason is the availability of extensions. Those allow you to block ads on the mobile web too and use Youtube in the backround.
Header Image: Digital Chains by stanjourdan (CC-BY-SA-2.0)
0 0 [Less]
|
Posted
over 1 year
ago
by
Pavel Rojtberg
This post will be for those of you that care about privacy – i.e. if you want that information about you is exclusively under your control.In that context not only Google is to blame, but actually most of the cloud services we know and use today.
... [More]
Still Google will serve us as a nice placeholder as it is the market-leader when it comes to providing free services in exchange for your user-profile that Google in turn uses to sell target advertising. Even if you are fine with that, Google is also infamous for killing services – which might hit the one you rely on eventually.
As the world is moving mobile-first, a prerequisite for replacing a service is that we can easily integrate the replacement with an Android device.Some might wonder why I chose Android here, given that it is made by Google. See, the problem is not who makes a service/ device, but who controls it. And with Android the main leverage for Google is bundling its services. If you take them away, the device itself is fully under your control – in contrast to Apple/ iOS.
Now, that we set the stage, lets start with:
Nextcloud
At the heart of our efforts will be Nextcloud. This started as an open-source alternative to Dropbox/ Google Drive, but is nowadays grown into a platform for a plethora of different services.The main selling-point is that you can just install Nextcloud on your own machine – ensuring that your data stays private. The software being Open-Source also means that it is not under control of a single corporation – in fact Nextcloud was forked from its predecessor Owncloud after a corporation tried to put the screws on its users.
Note, that if you are serious about this, you will need to invest around 500€ to get a machine for hosting that is decently fail-safe. If you rather want to be cheap, you can also just use a RaspberryPi to get away with less than half of that amount. For inspiration, you can take a look on the built I use or on the list of commercial nextcloud device providers.
Files & Photos
By using the Nextcloud Android App, you can directly replace Google Drive/ Dropbox as this is the core functionality of Nextcloud.
Additionally, the App allows you to automatically back-up your Photos/ Videos and free the local storage so you can stop using Google Photos too.
Contacts & Calendar
Nextcloud also supports Contacts and Calendar out of the box. To integrate them with into your Android Device there is the DAVx5 app. This app will function as an additional data provider, so you can just continue using the stock Google Contacts and Calendar apps. Those will, however, stop sending any data to Google.
This is especially useful, if you run a small-business and must ensure that your customer data is private according to the CCPA/ GDPR.
Nextcloud News
An often overlooked part of your privacy is Google News (also part of Google Now). Each time you view an article there, Google can mine your interests and political views – similar to what Facebook does. And by now you should know where these things can lead.
Another drawback here is that the Google Algorithm will create a bubble for you by only showing content coherent with your current world-view.I still prefer to manually choose the news sources – to create that bubble myself.
To do that, one usual subscribes some Web-feeds using a Feed aggregator like the Feedly service – similar to what Google Reader used to offer, before being killed by Google.
The go-to app here is Nextcloud News combined with the Android App for a pleasant mobile usage.
Music
Most of you are probably just stream music via Spotify/ Youtube Music, but keep in mind that these services merely rent the songs and as such they can arbitrarily disappear from your library.
Therefore, I like to have my own copy of the song. Unfortunately it is very inconvenient to juggle mp3s around for getting the music to various devices.
Google Play Music used to offer best of both worlds for me, where you could upload your own music files and manage your playlists in one place. Additionally, you could make the music available offline by pinning individual playlists on your device.Unfortunately, that concept did not allow Google to arbitrarily inject ads into my music stream and therefore the service got killed as well.
Nextcloud Music to the rescue! This app picks up your music library via Nextcloud files and allows to stream that via the Browser or the Subsonic API.This is where the Ultrasonic Android Player takes over. As with Play Music, you can either stream the library or pin individual songs/ playlists for offline use.
Other
If you clicked on the links above, you probably noticed the F-Droid alternative app store for Android.Getting your apps there ensures that you are using verified packages and open-source software. You can easily use it alongside with the Play Store. If that is too inconvenient for you, all of the above apps are also available via the Play Store.
Finally, there is the web-browser. If you do not log in with your Google Account, using Chrome is mostly safe. However, I suggest switching to Firefox. See the my original article on that topic for details. In short; the main reason is the availability of extensions. Those allow you to block ads on the mobile web too and use Youtube in the backround.
Header Image: Digital Chains by stanjourdan (CC-BY-SA-2.0)
0 0 [Less]
|