I own a Fairphone 4 with the stock camera apk that I extracted from official firmware in 2023-11. In 2024-04, Fairphone released a major camera update and I wanted to get it on my LineageOS-based system.
Problem
A forum post describes that the .apk extracted from the firmware fails to install:
adb: failed to install FPCamera.apk: Failure [ INSTALL_FAILED_MISSING_SHARED_LIBRARY: Reconciliation failed...: Reconcile failed: Package com.fp.camera requires unavailable native shared library libtctcameraalgo_jni.tct.so; failing!]
The new camera app (v7.00.04.0007.7.0)requires a shared library that is not available on my LineageOS firmware.
Extracting libtctcameraalgo_jni.tct.so
I extracted the current FP4-TP2L-factory.zip firmware image just as described in my previous blog post with unzip, simg2img and lpunpack. Then I mounted all partitions and looked into their file lists; it was system_ext that contained the library at
system_ext_a/lib64/libtctcameraalgo_jni.tct.so
(40kiB, md5 hash 3daaf896f43a33c7a883f57c7c4be3d5).
How not to modify the system partition
After running adb root I remounted the system_ext partition to be writable:
$ adb shell mount -o rw,remount /system_ext
This was how you did it in the pre-Android 10 days, and it does not work anymore because of dynamic Android partitions which are just as large as they need to be to contain the data:
$ adb push libtctcameraalgo_jni.tct.so /system_ext/lib64/ libtctcameraalgo_jni.tct.so: 1 file pushed, 0 skipped. 53.5 MB/s (40792 bytes in 0.001s) adb: error: failed to copy 'libtctcameraalgo_jni.tct.so' to '/product/lib64/libtctcameraalgo_jni.tct.so': remote write failed: No space left on device $ adb shell FP4:/ # df -h /system_ext/ Filesystem Size Used Avail Use% Mounted on /dev/block/dm-3 392M 390M 1.2M 100% /system_ext
I opened a thread on reddit and was told by TimSchumi that I should use adb remount. That command creates overlayfs mounts that lets you modify the system partitions by modifying an "overlay" directory, which is then loaded onto the actual partition.
I also found the magic_overlayfs Magisk module, but deemed that using native tools would be easier. It had a nice short explanation for the problem, though:
On Android 10+, system partitions might no longer be able to remount as read-write. For devices using dynamic partitions, it is nearly impossible to modify the system partiton as there is no space left.
Remounting
$ adb remount AVB verification is disabled, disabling verity state may have no effect Using overlayfs for /system Using overlayfs for /system_ext Using overlayfs for /product Using overlayfs for /vendor Using overlayfs for /odm [libfs_mgr] __mount(target=/system,flag=MS_PRIVATE)=-1: Invalid argument Remounted /system as RW Remounted /system_ext as RW Remounted /product as RW Remounted /vendor as RW Remounted /odm as RW Remounted /vendor/dsp as RW Overlayfs enabled. Remount succeeded Now reboot your device for settings to take effect
Simply rebooting, creating the lib64 directory in the upper layer of the overlay and pushing the .so file into it did not work:
$ adb reboot $ adb shell mkdir /mnt/scratch/overlay/system_ext/upper/lib64/ $ adb push libtctcameraalgo_jni.tct.so /mnt/scratch/overlay/system_ext/upper/lib64/ $ adb shell FP4:/ $ ls /system_ext/lib64 ls: /system_ext/lib64: Operation not permitted 1|FP4:/ $ ls -lah /system_ext/ total 63 drwxr-xr-x 2 root root 4.0K 2009-01-01 01:00 lib d????????? ? ? ? ? ? lib64 FP4:/ $ ls -lah /system_ext/lib64/ total 5.0K drwxrwxrwx 1 root root 3.4K 2024-11-13 21:49 . drwxr-xr-x 1 root root 3.4K 2024-11-13 21:46 .. -rw-r--r-- 1 root root 256K 2009-01-01 01:00 com.qualcomm.qti.dpm.api@1.0.so -rw-r--r-- 1 root root 141K 2009-01-01 01:00 lib-imsvideocodec.so -rw-r--r-- 1 root root 259K 2009-01-01 01:00 lib-imsvt.so -rw-r--r-- 1 root root 15K 2009-01-01 01:00 lib-imsvtextutils.so -rw-r--r-- 1 root root 32K 2009-01-01 01:00 lib-imsvtutils.so [...] -????????? ? ? ? ? ? libtctcameraalgo_jni.tct.so
After modifying the upper layer directory, I had to reboot the phone every time to make Android see the changes properly. The reboots fixed the permission problems.
Library registration
When libtctcameraalgo_jni.tct.so was at the right place, installation still failed with
Package com.fp.camera requires unavailable native shared library libtctcameraalgo_jni.tct.so
I then read that libraries need to be registered - pm list libraries did not list it.
In order to add library to vendor partition, you need to create text file which lists all added libraries. Since Android N (7.0) or later, apps are only allowed to access libraries on a specific whitelist of NDK libraries.
We can create counterpart file for vendor and create it in following path: /vendor/etc/public.libraries.txt.
I moved the library from system_ext/lib64 to vendor/lib64/ because that one already had a etc/public.libraries.txt that I could modify:
$ adb shell 1|FP4:/mnt/scratch/overlay/vendor/upper/etc # echo libtctcameraalgo_jni.tct.so >> public.libraries.txt
Then I rebooted, and all broke down. The phone was stuck at the "Fairphone" boot logo and would not advance any further.
Things I tried
Fastboot and recovery were available, but I did not find a way to disable the remount overlays from there.
The previously linked Implement dynamic partitions writes
For developers using eng or userdebug builds, adb remount is extremely useful for fast iteration. Dynamic partitions pose a problem for adb remount because there is no longer free space within each file system. To address this, devices can enable overlayfs. As long as there is free space within the super partition, adb remount automatically creates a temporary dynamic partition and uses overlayfs for writes. The temporary partition is named scratch, so don't use this name for other partitions.
I tried to delete the partition with fastboot, but that did not help:
$ fastboot delete-logical-partition scratch Deleting 'scratch' OKAY [ 0.009s] Finished. Total time: 0.009s
In recovery adb, I also tried to delete the scratch partition but did not even find it. lptools_new allows to list dynamic partitions:
FP4:/ # lptools_new_arm64 --get-info Slot: 0 Suffix: _b Path to super: /dev/block/by-name/super Group: qti_dynamic_partitions_b Arguments: --get-info GroupInSuper->qti_dynamic_partitions_b Usage->5422981120 TotalSpace->6441402368 NamePartInGroup->odm_b Size->1589248 NamePartInGroup->product_b Size->617897984 NamePartInGroup->system_b Size->971485184 NamePartInGroup->system_ext_b Size->416059392 NamePartInGroup->vendor_b Size->699158528 FP4:/ # lptools_new_arm64 --get-info --group cow Slot: 0 Suffix: _b Path to super: /dev/block/by-name/super Group: cow Arguments: --get-info GroupInSuper->cow Usage->5422981120 TotalSpace->6441402368 NamePartInGroup->odm_b-cow Size->1601536 NamePartInGroup->product_b-cow Size->620318720 NamePartInGroup->system_b-cow Size->975286272 NamePartInGroup->system_ext_b-cow Size->417689600 NamePartInGroup->vendor_b-cow Size->701894656
Listing the partitions with fastboot is not directly possible, but partitions can be inferred from the variables:
$ fastboot getvar all (bootloader) partition-size:super:0x180000000 [...] (bootloader) partition-type:product_b:raw [...]
No scratch there, though.
Bitter end
After 3 days I decided to give up and reinstalled the whole operating system. The SeedVault backup automatically reinstalled most applications, but unfortunately some of the applications I use do not support backing up their data:
- AntennaPod: Store list of subscriptions (possibly settings) through Google's Auto Backup for Apps #1899
- DAVx5: Allow client-side encrypted backups #259
- K-9 Mail: Add support for Android's backup mechanism #3857
- Nextcloud: Ability to backup / restore app settings #11090
Conversations did support backups, though.