The latest posts in full-text for feed readers.
I'm using a Fairphone 4 smart phone, running the MicroG-enabled LineageOS instead of the default software (ROM).
Unfortunately, the phone has several shortcomings:
Sometimes the touch screen triggers scrolls, swipes and zoom gestures without anyone touching it.
Thousands of FP4 users had this problem, and it was fixed in the official Fairphone software version Fairphone OS version B.086:
Ghost touches: an issue that causes the user interface to move or jump without touching the screen.
Unfortunately the fix is not available on LineageOS 20 (2024-01-05), which means I still experience the problem.
I once disassembled the phone, "cleaned" the display contacts by blowing onto it and re-assembled it. Afterwards the ghost touches appeared less often.
Enabling "touch visualization" in the Android developer settings gave me this screenshot of ghost inputs:
Sometimes the touch screen does not work (does not recognize touches) when the phone is lying flat on a table or my lap. When touching the frame it becomes responsive again.
Other people also reported this problem:
All we know is that there is some empirical evidence that the FP4’s touchscreen now requires the phone to be connected to some larger conductive body to reliably work – Either the user also touching the phone’s chassis, or the phone being connected to something else by its USB cable (like while charging it).
KurtF, 2023-08-26, Software update: FP4.SP2J.B.086.20230807
The Fairphone 3 already had this problem:
Seit ich das FP3+ habe funktioniert das Touch Display nicht,
wenn ich es auf den Tisch lege.
Wenn ich es in die Hand nehme, ist alles in Ordnung.
Lotta_Hund, 2021-05-06,
FP3+ Touch Display funktioniert nicht, wenn es liegt
I can't listen to podcasts via the Fairphone 4's built-in speakers because the sound is awful. My ring tone and message notification sounds also sound very tinny.
This hardware problem is known and acknowledged by the Fairphone developers:
Time to call it. It’s a hardware problem that we could mitigate in software by artificially reducing the volume of the top speaker. We believe that a blanket volume reduction will be perceived negatively by the majority of users, so we opted to leave it as it is.
The workaround described above (Accessibility Settings > Mono > balance all the way to the left) works in most cases. [...]
We’ve learned a lot from this investigation and we’ll use these lessons when designing future products.
Yasen_Tomov, 2023-06-08, Check for and replace blown speaker
For me the workaround is to use the Android accessibility settings to downmix all sounds to mono and move them to the right speaker (bottom of the phone), completely bypassing the right speaker (top of the phone).
I love to use SkyMap (F-Droid) to see which stars are visible in the sky at night.
Unfortuantely the compass on the Fairphone 4 is so bad that the map just jumps around and shows a totally different direction when rotating the phone for a couple of degrees.
As usual, other people have the same problem:
In almost all cases, the orientation error ranges from 45 to 180 degrees.
ChristianH, 2023-01-25, FP4: The compass is unreliable
Moving the phone in a "8"-pattern several times while Skymap or the compass app is open did improve accuracy quite a bit, but I have to recalibrate every time I use Skymap.
Published on 2024-02-25 in android, hardware
I bought a used Fairphone 4 and installed LineageOS with MicroG on it after unlocking the bootloader. One of the original Fairphone apps I wanted to use was the stock camera app "FPCamera.apk" (com.fp.camera). At first I extracted it from the official firmware download FP4-TP1V-factory.zip with the help of simg2img (from android-sdk-libsparse-utils) and lpunpack:
$ unzip FP4-TP1V-factory.zip images/super.img
$ file super.img
super.img: Android sparse image, version: 1.0, Total of 1572864 4096-byte output blocks in 226 input chunks.
$ simg2img super.img super.nonsparse.img
$ file super.nonsparse.img
super.nonsparse.img: data
$ binwalk super.nonsparse.img
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
1048576 0x100000 Linux EXT filesystem, blocks count: 277, image size: 283648, rev 1.0, ext2 filesystem data, UUID=f9a4283a-08b7-55e3-b63b-e404b5acb5ac, volume name "odm"
[...]
$ lpunpack super.nonsparse.img extracted
Extracting partition [odm_a] .... [ok]
Extracting partition [odm_b] .... [ok]
Extracting partition [product_a] .... [ok]
Extracting partition [product_b] .... [ok]
Extracting partition [system_a] .... [ok]
Extracting partition [system_b] .... [ok]
Extracting partition [system_ext_a] .... [ok]
Extracting partition [system_ext_b] .... [ok]
Extracting partition [vendor_a] .... [ok]
Extracting partition [vendor_b] .... [ok]
$ cd extracted
$ file system_a.img
system_a.img: Linux rev 1.0 ext2 filesystem data, UUID=2aa70c42-cce6-5610-9956-5fcc24a421ec (extents) (large files) (huge files)
$ mkdir system_a
$ sudo mount -o ro system_a.img system_a
$ cp system_a/system/priv-app/FPCamera/FPCamera.apk .
Then I installed FPCamera.apk onto the LineageOS Fairphone with adb install. Upon starting the camera app, I asked for some permissions and then said:
Permissions denied. You can change them in Settings > Apps.
I already had given it all permissions it asked for and that were selectable: Camera, Microphone, Location.
adb logcat showed that it asks for another permission WRITE_EXTERNAL_STORAGE:
CAM_PermissionUtils: checkPermissions, [android.permission.WRITE_EXTERNAL_STORAGE] ActivityTaskManager: START u0 {act=android.content.pm.action.REQUEST_PERMISSIONS pkg=com.android.permissioncontroller cmp=com.android.permissioncontroller/.permission.ui.GrantPermissionsActivity (has extras)} from uid 10190 CompatibilityChangeReporter: Compat change id reported: 174042980; UID 10190; state: DISABLED CAM_QuickActivity: onCreate return, by permission check!
It turns out that Android 13 does not allow apps to ask for that permission anymore as described in Android Developers: Granular media permissions and WRITE_EXTERNAL_STORAGE:
Note: If your app targets Build.VERSION_CODES.R (Android 11) or higher, this permission has no effect.
So the Fairphone camera app has a higher target but the code was not adjusted - on the Fairphone OS, this is not needed because Camera is a system app with pre-allowed permissions in system/etc/permissions/privapp-permissions-platform.xml.
The solution is to manually give the permission via adb:
$ adb shell pm grant com.fp.camera android.permission.WRITE_EXTERNAL_STORAGE
The Fairphone Camera app expects "Google Photos" to be installed, otherwise you cannot view the taken photos. Installing the camera proxy app solved that problem.
Published on 2023-11-11 in android
One of the few OUYA games that my ouya-plain-purchases Xposed module did not unlock was Final Fantasy III: Its developers wrote code that makes the game crash when a running Xposed module is found.
Now nearly two years after my investigation, OUYA Saviors member ZacharyFoxx found a solution: Convert the Xposed module from a normal user-installed app into a system app.
The only difference between them is that user applications are installed in /data/app/, while system apps are located in /system/app/. The /system/ partition is mounted read-only on the OUYA, so it takes some more steps to move it.
If plain purchases is not already installed, use the OUYA interface and go to Make > Tutorials and install it.
If you already enabled the plain purchases module, you have to disable it at first in the Xposed settings.
Now connect your OUYA via USB to your computer and use adb to execute the following steps:
$ adb shell
$ su
$ mount -o rw,remount -t ext4 /dev/block/platform/sdhci-tegra.3/by-name/APP
$ cp /data/app/de.cweiske.ouya.plainpurchases-1.apk /system/app/
$ rm /data/app/de.cweiske.ouya.plainpurchases-1.apk
$ mount -o ro,remount -t ext4 /dev/block/platform/sdhci-tegra.3/by-name/APP
$ exit
$ exit
$ adb reboot
The issue can finally be closed.
Update 2023-11: One user mentioned that he had to disable the OUYA Xposed "Mod Collection" to get it working (after rebooting).
Published on 2023-01-10 in android, blackbox, ouya
I got a used Blackview BV4900S Android smartphone and wanted to prepare it properly so it can be used.
The Blackview website does not have a download section: I had to contact customer support via e-mail (customerservice1@) to get the firmware file and installation tool.
They sent a link to the firmware file BV4900s_EEA_L620_V1.3_20220518V10 - uploaded to Mega instead of their own website... Another link was for the firmware install tool UpgradeDownload_R26.21.4101.7z. The instructions they sent were for a different tool, though.
The BV4900S has a Spreadtrum Unisoc SC9863A chipset, unlike the BV4900 and BV4900 Pro which both have a MediaTek chipset.
The official tool to flash .pac firmware files on Unisoc phones is SPD Flash Tool, the executable is named UpgradeDownload.exe.
On Windows 7 I had to install the Spreadtrum SPD drivers that I got from AndroidFileHost: SPD_Driver_R4.20.4201.zip. The SPD Flash Tool site has them as well, but their download was abysmal slow.
The official SPD version SPD_Upgrade_Tool_R27.23.1902.zip did not
work on my Windows 7 installation;
it failed with CFWDL-driver error
.
Instead I used the SPD version R26.21.4101 I got from Blackview,
which worked fine.
The installation steps were:
When loading a .pac file, SPD Flash Tool actuall extracts its contents into a subdirectory, in my case PAC_s9863a1h10_DownloadFiles_BV4900s_EEA_L620_V1.3_20220518V10_718_19C9C_0:
boot.img cache.img dtbo.img fdl1-sign.bin fdl2-sign.bin FileList.ini gnssbdmodem.bin gnssmodem.bin odmko.img PackInfo.csv persist.img PM_sharkl3_cm4_v2.bin prodnv.img s9863a1h10.xml SC9600_sharkl3_pubcp_modem.dat sharkl3_cm4.bin sharkl3_pubcp_deltanv.bin sharkl3_pubcp_DM_DSP.bin sharkl3_pubcp_LTEA_DSP.bin sharkl3_pubcp_nvitem.bin sml-sign.bin socko.img super.img teecfg-sign.bin tos-sign.bin u-boot-sign.bin u-boot-spl-16k-sign.bin unisoc_HD_720_1440_24bit.bmp 'unisoc_HD_720_1440_24bit.bmp(1)' userdata.img vbmeta_product.img vbmeta-sign.img vbmeta_system_ext.img vbmeta_system.img vbmeta_vendor.img
s9863a1h10.xml contains the partition list.
Being a cheap chinese smart phone, there is no Lineage OS port for it. So I had to take the stock firmware, but at least wanted root access to I could install an ad blocker.
I wanted to install Magisk to get root. The boot image was extracted by SPD Flash Tool, and the Magisk App was able to patch the boot image. But: Flashing the boot image is not possible.
I found two ways to access fastboot:
Flashing the boot image fails because the boot loader is locked:
$ fastboot flash boot ../magisk_patched-26300_weTYA.img Sending 'boot_a' (65536 KB) FAILED (remote: 'Download is not allowed on locked devices') fastboot: error: Command failed
In Android developer settings I enabled "OEM unlocking" and then tried to unlock the bootloader, but all commands failed:
$ fastboot flashing unlock FAILED (remote: 'Unrecognized command flashing unlock') fastboot: error: Command failed $ fastboot flashing unlock_critical FAILED (remote: 'Unrecognized command flashing unlock_critical') fastboot: error: Command failed $ fastboot getvar unlocked unlocked: no
Other Spreadtrum devices could be unlocked by fetching some token and signing the custom firmware with it. The command does not work on this phone:
$ fastboot oem get_identifier_token ... OKAY [ 0.001s] finished. total time: 0.001s $ fastboot oem invalidcommand ... OKAY [ 0.001s] finished. total time: 0.001s
Blackview support says they do not support unlocking their devices. I asked on XDA for help but don't really expect any outcome.
For now I am stuck; the BV4900S cannot be unlocked.
dmesg output:
usb 1-2: new high-speed USB device number 63 using xhci_hcd usb 1-2: New USB device found, idVendor=18d1, idProduct=4ee8, bcdDevice= 4.04 usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-2: Product: BV4900s usb 1-2: Manufacturer: Chinoe usb 1-2: SerialNumber: BV4900sEEA00001817
usb 1-2: new high-speed USB device number 64 using xhci_hcd usb 1-2: New USB device found, idVendor=1782, idProduct=4ee0, bcdDevice= 4.14 usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-2: Product: Unisoc Phone usb 1-2: Manufacturer: Unisoc usb 1-2: SerialNumber: BV4900sEEA00001817
Published on 2023-10-22 in android, bigsuck
I had to restore my wife's smart phone files from a manual backup after the upgrade from LineageOS 16 to 20 failed. After restoration, all images in the gallery were grouped into the same date - there was no order whatsoever.
Simple Gallery Pro sorts and groups images by their file modification date, not by their EXIF date. Because I restored all 9.000 files on the same day, they had the same modification date and thus were grouped into the same day :(
I made the backup with adb pull /sdcard, which copied all files from the smartphone onto my PC. This command already dropped the original modification dates; the better backup command would have been
adb pull -a /sdcard
- that one preserves the file timestamps.
At first I looked for a solution to extract timestamps from the photo's EXIF data and change the file's creation timestamp.
Then I learned that images and videos received by WhatsApp have no exif data, but have filenames that include the timestamp. Those files need also be processed.
In the end I installed Image & Video Date Fixer by JD Apps, which fixes timestamps by extracting dates from EXIF data and file names. The free version only fixes 50 files, but the premium one could fix all files in all folders in one go.
Published on 2023-10-22 in android
In 2019 I installed LineageOS 16 on a Xiaomi Mi8 smartphone, and now in 2023 I wanted to upgrade to LineageOS 20 - skipping multiple major versions.
Installing the update to the system partition worked, but the system was broken afterwards.
When booting the first time, it tried to apply migrations: System databases and files in the old format need to be modified so they are structured as the new operating system version expects it. Unfortunately, the LineageOS 20 firmware is not able to upgrade from version 16 - my guess is that the migrations for such "old" versions are missing.
The phone booted to the lock screen, the screen flickered a couple of times and then it rebooted. Repeat forever.
The LineageOS developers do not keep older versions around; so I did not have the chance to first flash version 17, then 18, 19 and at last 20. The only downloadable firmwares were 19 and 20.
In preparation for the update I made two backups:
I booted into the TWRP 3.7.0_9-0 recovery partition with adb reboot recovery. Then I opened the backup settings, selected the boot, system and data partitions and ran
$ adb backup --twrp
Then it took some time while the backup was directly stored on my PC.
While I was in TWRP recovery mode, I pulled all the user files from the phone:
$ adb pull /sdcard
Later I learned that I should have used pull -a to backup the file timestamps as well.
After the upgrade from 16 to 20 failed, I wanted to restore my partition backup so that the phone would be back on Lineage 16. I booted into recovery, probably selected "restore" and then executed this on my PC:
$ adb restore backup.ab
Boot and system partitions got restored, but the data partition failed:
extractTarFork() process ended with ERROR: 255
Running the restoration twice did not make it better. It turned out many other people had the same problem: Team-Win-Recovery-Project #964.
In the end we had to install LineageOS 20 freshly and manually install and setup all the apps that were previously on the phone :( Very big suck.
The failed system restore led to other problems:
Updating the system partitions with files from the official firmware updates with fastboot fails now for some partitions:
$ fastboot flash abl_ab abl.elf Warning: skip copying abl_ab image avb footer (abl_ab partition size: 0, abl_ab image size: 155648). Sending 'abl_ab' (152 KB) OKAY [ 0.014s] Writing 'abl_ab' OKAY [ 0.007s] Finished. Total time: 0.032s
This did not change even after I installed and setup the official MIUI firmware.
When booting into recovery TWRP and selecting "reboot" there, it warns me that No OS Installed! Are you sure you wish to reboot? (even though the OS boots fine).
TWRP does not ask me to unlock the data partition anymore.
I guess this began when I once cancelled the unlock process when TWRP was booted up, but I am not sure.
This means no data recovery with TWRP anymore :(
Published on 2023-10-17 in android, bigsuck
A factory-reset Razer Forge TV micro gaming console requires you to sign into a Google account, but that always fails with an error message:
Couldn't sign in
Your username and password don't match. Please verify them and try again.
(I guess this is because the old Android 6-based firmware uses an API that Google shut off.)
I looked for a way to skip the login requirement and looked through the system applications in the firmware image, to find the .apk that contains the error message. Unfortunately the system applications are split into .apk and .odex files that needed to be combined before I could analyze them.
SetupWraith is the app responsible for the initial setup process, and as usual I used jadx-gui to browse its decompiled source code:
package com.google.android.tungsten.setupwraith.ui;
public class AccountFragment {
[...]
private void launchAccountSetup() {
[...]
Partner p = Partner.get(getActivity());
extrasBundle.putBoolean("showSkip", p.getBoolean("show_skip_signin"));
[...]
}
}
So there is an option to skip setup! The "Partner" class is interesting, because it loads settings from a different app:
package com.google.android.tungsten.setupwraith.util;
public class Partner {
[...]
public static synchronized Partner get(Context context) {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent("com.google.android.tvsetup.action.PARTNER_CUSTOMIZATION");
for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
[...]
String packageName = info.activityInfo.packageName;
try {
Resources res = pm.getResourcesForApplication(packageName);
sPartner = new Partner(packageName, res, context);
}
[...]
}
}
[...]
public boolean getBoolean(String idName) {
int pId;
if (this.mPackageName != null && (pId = this.mResources.getIdentifier(idName, "bool", this.mPackageName)) != 0) {
return this.mResources.getBoolean(pId);
}
int pId2 = this.mDefaultResources.getIdentifier(idName, "bool", this.mDefaultPackageName);
if (pId2 != 0) {
return this.mDefaultResources.getBoolean(pId2);
}
return false;
}
}
It loads the resources of the first application that listens for the com.google.android.tvsetup.action.PARTNER_CUSTOMIZATION intent. When asked for a setting, it tries to load it from that application's settings, and falls back to its own default in case of failure.
Knowing about that intent, I quickly found an example SetupCustomizer application that I used as base for my own forge-setup-customizer app.
I installed it on the system partition with a fastboot-enabled TWRP for the Razer Forge TV and booted the system:
Published on 2023-10-12 in android
After building my own boot image for the Android 6 based Razer Forge TV and patching adb to allow root I noticed that I still cannot access the /data directory:
root@pearlyn:/ # ls -l /data opendir failed, Permission denied root@pearlyn:/ # id uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:shell:s0
adb logcat showed the following:
10-01 17:30:23.079 2427 2427 W sh : type=1400 audit(0.0:30): avc: denied { dac_override } for capability=1 scontext=u:r:shell:s0 tcontext=u:r:shell:s0 tclass=capability permissive=0
So the problem is that despite being root, my SELinux context u:r:shell:s0 prevents me from doing all the things I want to.
You can check if SELinux is enabled with getenforce:
root@pearlyn:/ # getenforce Enforcing
I tried different things to disable SELinux, but all of them failed:
Modify kernel cmdline parameter in the boot image to disable SELinux:
enforcing=0 androidboot.selinux=permissive
Disable SELinux in adb shell:
setenforce permissive
Adding this to the boot image's init.rc did also not help.
Disable some SELinux specific commands in the boot image's init.rc:
setprop selinux.reload_policy 1 restorecon_recursive /data
Give adbd root capabilities in init.rc by changing u:r:adbd:s0 to u:r:su:s0:
service adbd /sbin/adbd --root_seclabel=u:r:su:s0 seclabel u:r:su:s0
In this case, I could not connect via adb anymore:
connection refused
, which means adbd could not start.
I could not access /data whatever I did.
Then I found out that SELinux cannot be disabled in user mode firmware builds! The Android documentation states:
SELinux enforcement can be disabled via ADB on userdebug or eng builds. To do so, first switch ADB to root by running adb root. Then, to disable SELinux enforcement, run:
adb shell setenforce 0
The Razer Forge TV firmware M-144 is a user build, though:
root@pearlyn:/ # getprop ro.build.type user
There is no way to disable SELinux on user builds unless you build your own kernel and probably the SELinux library.
Published on 2023-10-05 in android
The Razer Forge TV was an Android-TV based micro console and the sole reason that Razer bought OUYA - to make the games library available on the Forge TV. It was discontinued in 2016, the server was shut down in 2019.
I'm now trying to resurrect its "Corex" store just as I did with the OUYA and the MadCatz Mojo.
When starting a factory reset Forge TV with firmware M-144, the Android TV setup wizard appears and requires you to sign in with a Google Account. Entering an e-mail address and password does not work anymore in 2023, probably because Google removed the necessary APIs.
People say that it is possible to sign in with a second Android device that is near the Forge. I don't have an Android phone with the "Google" app required for this and am stuck.
A way around that setup wizard would be to mark the setup as completed, so that it does not start at all. This requires debugging access via adb, which I don't have.
So I need to modify the boot image to enable adb.
Razer published two firmware versions, of which I chose the newer one:
The firmware image is a .zip file with some partitions inside:
$ unzip -l ../image-forge-m-144.zip
Archive: ../image-forge-m-144.zip
Length Date Time Name
--------- ---------- ----- ----
22 2016-11-09 10:36 android-info.txt
14630912 2016-11-09 10:37 boot.img
15351808 2016-11-09 10:37 recovery.img
978758664 2016-11-09 10:38 system.img
--------- -------
1008741406 4 files
boot.img seemed to be a standard boot image:
$ file boot.img
boot.img: Android bootimg, kernel (0x8000), ramdisk (0x1000000), page size: 4096, cmdline (console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x3b7 dwc3_msm.cpu)
I used abootimg (There is a Debian package!) to extract the contents:
$ abootimg -x ../boot.img
writing boot image config in bootimg.cfg
extracting kernel in zImage
extracting ramdisk in initrd.img
$ file initrd.img
initrd.img: gzip compressed data, from Unix, original size modulo 2^32 1845504
$ file zImage
zImage: Linux kernel ARM boot executable zImage (little-endian)
The file system could be extracted with abootimg-unpack-initrd which is part of the abootimg Debian package:
$ abootimg-unpack-initrd initrd.img ramdisk
3605 Blöcke
$ ls ramdisk/
charger init.qcom.sh oem
data init.qcom.ssr.sh proc
default.prop init.qcom.syspart_fixup.sh property_contexts
dev init.razer.info.sh res
file_contexts init.razer.peripherals.sh sbin
fstab.qcom init.razer.ping.sh seapp_contexts
init init.razer.rc selinux_version
init.class_main.sh init.razer.usb.rc sepolicy
init.environ.rc init.razer.usb.sh service_contexts
init.mdm.sh init.rc sys
init.pearlyn.diag.rc init.target.rc system
init.qcom.class_core.sh init.trace.rc ueventd.qcom.rc
init.qcom.early_boot.sh init.usb.configfs.rc ueventd.rc
init.qcom.factory.sh init.usb.rc
init.qcom.rc init.zygote32.rc
After modifying default.prop to enable adb (more on that later) I had to regenerate a boot.img file and used abootimg-pack-initrd and mkbootimg.py:
$ abootimg-pack-initrd newinitrd.img ramdisk
$ ../mkbootimg.py --kernel cleanboot/zImage --ramdisk cleanboot/newinitrd.img --cmdline "console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x3b7 dwc3_msm.cpu_to_affin=1" --pagesize 0x1000 --kernel_offset 0x8000 --ramdisk_offset 0x1000000 --tags_offset 0x100 -o rebuilt-boot.img
Then I unplugged the Forge's HDMI cable and powered it on, which puts it into Fastboot mode.
Instead of flashing the whole firmware image .zip file with fastboot -w update image-forge-m-144.zip I only flashed the new boot.img file:
$ fastboot devices
171256710321511 fastboot
$ fastboot flash boot rebuilt-boot.img
$ fastboot reboot
I was very suprised that the Forge did not boot. It was stuck in a loop: Try to boot (ca. 1 minute), reboot and try again.
Via systematic tests I found that my mkbootimg.py command was correct, but the new initrd.img was broken - or at least not understood by the Forge TV.
The initrd.img is a gzip compress file, so I unpacked it first:
$ cp initrd.img initrd.cpio.gz
$ cp newinitrd.img newinitrd.cpio.gz
$ gunzip initrd.cpio.gz
$ gunzip newinitrd.cpio.gz
At first I looked at the file sizes:
$ ls *.cpio
1845504 17. Sep 18:15 initrd.cpio
1845760 17. Sep 18:31 newinitrd.cpio
The new image is a bit larger. Comparing the files with dhex showed massive differences.
Listing the contents of the .cpio archive showed that the new image has the local directory inside, which the original one had not:
$ cat initrd.cpio|cpio -t
charger
data
default.prop
[...]
$ cat newinitrd.cpio|cpio -t
.
charger
data
default.prop
[...]
Since abootimg-pack-initrd only uses cpio inside, I built my own cpio command chain to remove the dot:
$ cd ramdisk
$ find |grep -v '^.$' | sort | cpio --quiet -o -H newc > ../newinitrd2.cpio
$ cat ../newinitrd2.cpio|cpio -t
charger
data
default.prop
[...]
Comparing the .cpio files with dhex showed much less differences:
An image built on that file did not boot, though.
I did not want to learn the CPIO archive format and looked for a tool that could analyze them.
At first I tried ofrak, but it only showed header information and nothing about the file contents or the archive structure:
$ ofrak identify initrd.cpio
[ ofrak_cli.py: 173] No disassembler backend specified, so no disassembly will be possible
Identifying file: initrd.cpio
= Tags =
CpioFilesystem
FilesystemRoot
GenericBinary
File
FilesystemEntry
= Attributes =
AttributesType[FilesystemEntry](name=initrd.cpio, stat=os.stat_result(st_mode=33188, st_ino=10759612, st_dev=65027, st_nlink=1, st_uid=1000, st_gid=1000, st_size=1845504, st_atime=1695707951, st_mtime=1695707778, st_ctime=1695707931), xattrs={}) Magic:
Mime: application/x-cpio
Descriptor: ASCII cpio archive (SVR4 with no CRC)
It took 0.009 seconds to run the OFRAK script
Then I remembered binwalk and indeed got a nice overview on the cpio contents:
$ binwalk initrd.cpio
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ASCII cpio archive (SVR4 with no CRC), file name: "charger", file name length: "0x00000008", file size: "0x0000000d"
136 0x88 ASCII cpio archive (SVR4 with no CRC), file name: "data", file name length: "0x00000005", file size: "0x00000000"
252 0xFC ASCII cpio archive (SVR4 with no CRC), file name: "default.prop", file name length: "0x0000000d", file size: "0x0000023f"
[...]
$ binwalk newinitrd2.cpio
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ASCII cpio archive (SVR4 with no CRC), file name: "charger", file name length: "0x00000008", file size: "0x0000000D"
136 0x88 ASCII cpio archive (SVR4 with no CRC), file name: "data", file name length: "0x00000005", file size: "0x00000000"
252 0xFC ASCII cpio archive (SVR4 with no CRC), file name: "default.prop", file name length: "0x0000000D", file size: "0x0000023F"
[...]
One difference that struck me was that the file size was given in lowercase hex letters in the original file (0x0000000d), while it was in uppercase in my custom archive (0x0000000D).
I had no idea if that is covered by the specification, but it could be the source of the problem. Now I had to find a way to generate a cpio archive with lowercase file size indicators.
cpio was original part of the POSIX standard, but was abandoned in favor of pax. pax can read and generate cpio archives - and it shows more meta data than even binwalk!
$ pax -vf initrd.cpio
lrw-r--r-- 1 root root 13 Jan 1 1970 charger -> /sbin/healthd
drwxrwx--x 1 root root 0 Jan 1 1970 data
-rw-r--r-- 1 root root 575 Jan 1 1970 default.prop
[...]
$ pax -vf newinitrd2.cpio
lrwxrwxrwx 1 cweiske cweiske 13 Sep 26 07:25 charger -> /sbin/healthd
drwxrwx--x 2 cweiske cweiske 0 Sep 26 07:25 data
-rw-r--r-- 1 cweiske cweiske 575 Sep 26 07:25 default.prop
This was the tool I had hoped for when I began finding the differences, and now they were very clear: My new file had my username and group, while the original one only "root". My archive had file modification timestamps, the original only a "0".
Generating the cpio archive with pax generated a file much larger than the original one:
$ find |grep -v '^.$' | sed 's#^./##' | sort | pax -w -x sv4cpio -M mtime -M uidgid > ../paxinitrd.cpio
$ ls -l *.cpio
1845504 26. Sep 07:56 initrd.cpio
1845760 26. Sep 08:07 newinitrd2.cpio
2575360 26. Sep 08:30 paxinitrd.cpio
It turned out that pax added duplicates:
$ pax -vf paxinitrd.cpio
[...]
drwxr-xr-x 3 root root 0 Jan 1 1970 res/images
drwxr-xr-x 2 root root 0 Jan 1 1970 res/images/charger
-rw-r--r-- 1 root root 463 Jan 1 1970 res/images/charger/battery_scale.png
-rw-r--r-- 1 root root 1368 Jan 1 1970 res/images/charger/battery_fail.png
drwxr-xr-x 3 root root 0 Jan 1 1970 res/images
drwxr-xr-x 2 root root 0 Jan 1 1970 res/images/charger
-rw-r--r-- 1 root root 463 Jan 1 1970 res/images/charger/battery_scale.png
-rw-r--r-- 1 root root 1368 Jan 1 1970 res/images/charger/battery_fail.png
drwxr-xr-x 2 root root 0 Jan 1 1970 res/images/charger
-rw-r--r-- 1 root root 463 Jan 1 1970 res/images/charger/battery_scale.png
-rw-r--r-- 1 root root 1368 Jan 1 1970 res/images/charger/battery_fail.png
-rw-r--r-- 1 root root 1368 Jan 1 1970 res/images/charger/battery_fail.png
-rw-r--r-- 1 root root 463 Jan 1 1970 res/images/charger/battery_scale.png
[...]
The culprit for the duplicate files was pax' handling of directory names: find lists files and directories. pax then adds the files to the archive, but when it sees a directory it automatically adds all the files on its own - even though find lists the directory contents itself.
The -d option helps here - it causes pax to add the directory entry, but not the files inside:
$ find |grep -v '^.$' | sort| pax -w -x sv4cpio -M mtime -M uidgid -s '#^./##' -d -f ../paxinitrd2.cpio
The file was a tiny bit smaller than the original initrd.cpio, and the binwalk output was identical! dhex showed less differences than before:
And what's most important: The Razer Forge TV could boot that boot.img file!
Published on 2023-09-26 in android
My own OUYA API server has been running since november last year. The game data files have been worked on: Previously missing games were added, wrong developer UUIDs fixed. Many of the games got product data, so that purchases can be restored now and games stuck in demo mode unlock to the full version now.
All went nicely until end of january, people with factory reset OUYAs came into the chat and asked for help because they could not get past the network configuration screen. All they got were the following error messages:
Unable to connect
Please check your Internet connection and try again.
and
Cannot establish a connection with the OUYA Servers. To turn on Wi-Fi, unplug your Ethernet cable and proceed. To retry ethernet, please check your connection and then unplug and re-plug your cable.
Luckily I own two OUYAs, so I did a factory reset on the development machine and tried the setup myself: It failed with the same error messages as reported.
The initial setup is managed by OUYAOOBE.apk, the Out Of the Box Experience application. I read the decompiled sources and soon found the culprit, ConnectivityChecker.java:
public Void doInBackground(Void... params) {
Socket socket = null;
boolean reachable = false;
try {
Socket socket2 = new Socket("devs.ouya.tv", 80);
reachable = true;
if (socket2 != null) {
try {
socket2.close();
Socket socket3 = socket2;
} catch (IOException e) {
Socket socket4 = socket2;
}
}
} catch (UnknownHostException, IOException e2) {
// error handling
}
setResult(reachable);
return null;
}
So OUYA's setup application has a hard-coded check to devs.ouya.tv, which was the name of the API server - and it does not use the ouya config override file ouya_config.properties. The DNS record still exists:
$ dig devs.ouya.tv devs.ouya.tv. 60 IN CNAME tokyo-6663.herokussl.com.
But the host that the domain was aliased to does not exist anymore:
$ dig +short tokyo-6663.herokussl.com $
Apparently Razer's Heroku contract was cancelled or not renewed, and the box that served the redirect from devs.ouya.tv to razer.com was shutdown forever.
It is possible to get past the network selection screen by letting the OUYA think that devs.ouya.tv still exists, and let it point to some IP address that has a HTTP and HTTPS server running.
Choose one of the following options:
Summary: Temporarily change the OUYA's network configuration to use 178.254.13.17 as DNS server.
Some routers allow you to add own domain-name-to-IP-mappings. If your router is one of them, add a mapping for hostname devs.ouya.tv, status.ouya.tv and ouya-updates.s3.amazonaws.com to the IP of my server: 178.254.13.17.
Each computer has a file /etc/hosts that allows you to define DNS mappings. On Android, that file is located at /system/etc/hosts.
This method will surely work, but takes the most steps. You need to have the "Android Debug Bridge" (adb) command line tool installed on your PC. Have a look at Cyanogen Mod CM11 and the Ouya, it has installation instructions for adb on windows.
adb shell
su
to become the root user
mount -o rw,remount -t ext4 /dev/block/platform/sdhci-tegra.3/by-name/APP
echo "178.254.13.17 devs.ouya.tv status.ouya.tv ouya-updates.s3.amazonaws.com" >> /system/etc/hosts
The connectivity check should work now without rebooting.
If you still get the "Unable to connect" error, then you are missing the ouya_config.properties file.
Because I am moving to a new server, the IP address changed from 83.169.45.222 to 178.254.13.17.
Published on 2020-02-24 in android, network, ouya