Modifying an Android boot image

After my old nook reader died, I bought a Tolino Vision 4 HD ebook reader. It is usable without registration - as opposed to Kobo readers which you have to register. It is water proof enough to drop it into the bath tub, and it has thesauri and translation dictionaries. For 140€ I bought it at buecher.de.

The reader is based on Android 4.4.2, and it is rootable. Unfortunately the root patch is for firmware version 1.9.0, while I updated already to 12.2.0.

For the firmware update, I let it once into my wifi. During that time it also downloaded the latest recommendations from the buecher.de store, which it now shows on the home screen - taking the lower half of it - and after I finish reading a book:

Tolino shows ads on the homescreen Tolino shows ads after a book has been read

I wanted to get rid of this unsolicited advertisements, and asked support how to do so. They replied with "cannot be disabled" :/

I thought I could simply clear that cached recommendation data on my own but found that I could not simply plug the micro USB cable and run adb shell - the Tolino does not have adbd running :/

Activating adb without developer settings GUI

In a standard Android user interface I would open settings, tap seven times on the build number, and then activate ADB debugging in the now visible developer options.

The Tolino has its own GUI, and their settings UI has no ADB checkbox.

There is an alternative: Setting some properties in the boot image's default.prop file, which is what the ADB checkbox in the normal developer options does.

persist.service.adb.enable=1
persist.sys.usb.config=mass_storage,adb

The standard USB config for the Tolino was "mass_storage", so I only added ",adb" to it.

Now I needed to modify my Tolino's default.prop file.

Building my own boot image

I did not create a boot image from scratch, I only modified an existing one.

The Tolino Vision 4 update website had a .zip file for updating from 11.2.0 to 12.2.0, so I fetched and extracted that. Among others, it contained a 4.5 MiB boot.img file.

Unpacking boot.img

At first I tried sophiehuiberts/Bootimg-scripts but found out that the extracted kernel file does not have the correct size.

The Android core git repository up to commit a6abd821d contained two files in the mkbootimg directory: unpack_bootimg.py and mkbootimg.py, which I used in the end.

When extracting the boot image, the unpack script shows important meta data:

$ ./unpack_bootimg.py --boot_img boot-12.2.0.img --out ex-boot-12.2.0
boot_magic: ANDROID!
kernel_size: 3936664
kernel load address: 0x80808000
ramdisk size: 508752
ramdisk load address: 0x81800000
second bootloader size: 0
second bootloader load address: 0x81700000
kernel tags load address: 0x80800100
page size: 2048
boot image header version: 0
os version and patch level: 0
product name: ntx_6sl
command line args: console=ttymxc0,115200 init=/init androidboot.console=ttymxc0 max17135:pass=2, fbmem=6M video=mxcepdcfb:E060SCM,bpp=16 no_console_suspend
additional command line args:

Before actually modifying something, I rebuilt it until my repacked image was bit for bit equal to the original one.

With the help of the meta data above, I ended up with the following parameters for repacking:

$ mkbootimg.py --kernel ex-boot-12.2.0/kernel --ramdisk ex-boot-12.2.0/ramdisk\
  --cmdline "console=ttymxc0,115200 init=/init androidboot.console=ttymxc0 max17135:pass=2, fbmem=6M video=mxcepdcfb:E060SCM,bpp=16 no_console_suspend"\
  --board ntx_6sl\
  --kernel_offset 0x70808000 --ramdisk_offset 0x71800000\
  --second_offset 0x71700000 --tags_offset 0x70800100\
  -o rebuilt-boot-12.2.0.img

Flashing the boot image

I flashed my rebuilt boot image onto the Tolino to see if it works at all.

The Tolino must be in fastboot mode for flashing, and I got to there that way:

  1. Connect it via USB to your computer.
  2. Power off by holding the power button until the shutdown options appear and shut it down.
  3. The power button has its own white LED. Wait until it is off (~5 seconds after the screen is off), because only then it is really powered off.
  4. Hold the light button and then also press the power button. Wait ~3-4 seconds until the LEDs on the power button and the home button are on.
  5. Release the buttons.

The reader is in fastboot mode now and shows an empty screen only. The mode can be verified by listing the fastboot devices:

$ fastboot devices 
86346346,2f12e2342c894d9e8afa3cf5564223ac	fastboot

Flashing is easy now:

$ fastboot flash boot rebuilt-boot-12.2.0.img
$ fastboot reboot

If the Tolino starts up normally, then the rebuilt boot image works.

Unpacking ramdisk

The rebuilt boot image uses the original kernel and boot image files. default.prop is inside the ramdisk, so I had to extract it:

$ cd ex-boot-12.2.0/
$ mkdir ex-ramdisk
$ cd ex-ramdisk
$ gunzip -c ../ramdisk | cpio -i

This gave me a directory full of files:

$ ls
charger             init.E60Q50.rc         property_contexts
data                init.E60Q50.usb.rc     res
default.prop        init.E60Q60.rc         rle
dev                 init.E60Q60.usb.rc     RTL8189_init.rc
file_contexts       init.E60QF0.rc         sbin
fstab.E60K00        init.E60QF0.usb.rc     seapp_contexts
fstab.E60Q30        init.E60QH0.rc         sepolicy
fstab.E60Q50        init.E60QH0.usb.rc     sys
fstab.E60Q60        init.E60QJ0.rc         system
fstab.E60QF0        init.E60QJ0.usb.rc     ueventd.E60K00.rc
fstab.E60QH0        init.E70Q20.rc         ueventd.E60Q30.rc
fstab.E60QJ0        init.E70Q20.usb.rc     ueventd.E60Q50.rc
fstab.E70Q20        init.ED0Q00.rc         ueventd.E60Q60.rc
fstab.ED0Q00        init.ED0Q00.usb.rc     ueventd.E60QF0.rc
fstab.freescale     init.environ.rc        ueventd.E60QH0.rc
init                init.freescale.rc      ueventd.E60QJ0.rc
init.common.usb.rc  init.freescale.usb.rc  ueventd.E70Q20.rc
init.E60K00.rc      init.rc                ueventd.ED0Q00.rc
init.E60K00.usb.rc  init.trace.rc          ueventd.freescale.rc
init.E60Q30.rc      init.usb.rc            ueventd.rc
init.E60Q30.usb.rc  proc                   WC121A2_init.rc

I went the safe route and did not modify anything at this stage. I only re-packed the directory back to a ramdisk file:

$ find . | cpio -o -H newc | gzip > ../repack-ramdisk

Then I re-built the boot.img as shown above with the new ramdisk file, flashed it onto the reader and booted it up. All fine.

Enabling adb

Now that I knew that the whole boot image building process worked, I actually modified default.prop in the ramdisk folder and added the two lines:

persist.service.adb.enable=1
persist.sys.usb.config=mass_storage,adb

After building, flashing and booting my adb-enabled boot image I was finally able to use adb:

$ adb devices
List of devices attached
86346346,2f12e2342c894d9e8afa3cf5564223ac	no permissions (user in plugdev group; are your udev rules wrong?); see [http://developer.android.com/tools/device.html]

.. or not. I first had to add a new udev rule:

$ emacs /etc/udev/rules.d/99-tolino.rules
SUBSYSTEM=="usb", ATTR{idVendor}=="1f85", ATTR{idProduct}=="6052", MODE="0660", GROUP="plugdev", TAG+="uaccess"
 
$ udevadm control --reload

Then I unplugged and replugged the USB cable, and it finally worked:

$ adb devices
List of devices attached
86346346,2f12e2342c894d9e8afa3cf5564223ac	device
 
$ adb shell
shell@ntx_6sl:/ $ getprop | grep product
[ro.build.product]: [ntx_6sl]
[ro.product.board]: [EVK]
[ro.product.brand]: [RakutenKobo]
[ro.product.cpu.abi2]: [armeabi]
[ro.product.cpu.abi]: [armeabi-v7a]
[ro.product.device]: [ntx_6sl]
[ro.product.display]: [eink]
[ro.product.hardwareType]: [E60Q50]
[ro.product.locale.language]: [en]
[ro.product.locale.region]: [US]
[ro.product.manufacturer]: [Rakuten Kobo Inc.]
[ro.product.model]: [tolino]
[ro.product.name]: [ntx_6sl]

How I got root access is a different story.

Written by Christian Weiske.

Comments? Please send an e-mail.