Skip to content

Latest commit

 

History

79 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Working camera and bluetooth for LineageOS on the Amazon Echo Show 5 (1st and 2nd gen) and Echo Show 8 (1st gen)

Buy Me A Coffee

This repository makes the front camera fully functional on unofficial LineageOS 18.1 for the Amazon Echo Show 5 (1st gen, 2nd gen) and Echo Show 8 (1st gen) . The stock ROM port ships no camera stack at all; with this work applied, the device gets:

Feature Status
Camera enumeration (front, correct orientation) working
Live preview with correct colors working
Still capture (takePicture, camera2/CameraX) working, full 1600x1200 JPEG
Video mode sensor timing working
Lens shading correction (factory calibration) working
Auto exposure working
Adaptive auto white balance working, with a calibrated correction
Black level corrected (the stock tuning left a 30% grey floor)

Also fixed: Bluetooth on crown and cronos

The stock port leaves Bluetooth completely broken on these devices: the adapter crash-loops every ~8 seconds on enable (the A2DP sink overlay ships as a runtime RRO, so its services resolve as disabled while the profile list expects them), and every BLE scan fails with SCAN_FAILED_INTERNAL_ERROR because the android.hardware.bluetooth_le feature is never declared. patches/0017-device-tree-bluetooth-enablement.patch fixes both defects and keeps A2DP sink working; the patch header carries the full analysis.

The patch currently covers crown and cronos, where it is tested and verified (build-side and on hardware after a reflash: adapter holds ON, sink services running, both features declared). Whether checkers needs the same treatment is not yet known; its tree carries the same A2DP sink overlay, so if it shows the same symptoms the patch shows exactly what to change in its tree.

The camera working on an Echo Show 5, 2nd gen

camera-demo-720p.mp4

Actual frame quality

Everything here was reverse-engineered against a live device; the complete investigation, including every dead end, is in docs/findings.md. It is long, but if you maintain a MediaTek device port it is probably the most useful file in the repository.

Why this was hard, in one paragraph

The Echo Show ships an Android 7.1 (API 25) camera stack on an Android 9 era kernel and the LineageOS port runs Android 11 userspace on that kernel. Nothing agrees with anything: the blobs need symbols Android 11 removed, an older display framework than the ROM ships, a cmdq event encoding the kernel no longer speaks, and Amazon's kernel struct layouts rather than upstream MediaTek's. On top of that, the ROM's kernel selected the 1st gen camera sensor (OV9734) while the 2nd gen device has an OV02B10, so a sensor driver had to be ported, and all of MediaTek's image tuning (white balance, black level) is for the wrong sensor. Each of those is fixed here, layer by layer.

Supported devices

Device Codename Sensor Status
Echo Show 5 (2nd Generation) cronos OV02B10 working, verified end to end
Echo Show 8 crown OV9734 working, verified end to end - apply patches/0014 for the sensor flip
Echo Show 5 checkers OV9734 working, community verified - needs the pinned libdpframework build the install scripts now select

On the OV9734 devices apply only the kernel struct patch (0001), the sensor flip (0014) and the two privacy-latch patches (0015, 0016) - their sensor driver is already in the tree and selected - and skip the two color corrections in step 9, which exist only to undo cronos running an OV02B10 against OV9734 tuning. Three OV9734 bring-ups ran neither and got correct color, crown under natural daylight included, so those devices need no calibration at all.

OV9734 devices need patches/0014 for the sensor flip. Their driver selects its mirror setting on CONFIG_CAMERA_MULTIMODAL, which is default n and set by no Echo Show defconfig, so the flip Amazon intended for these devices is never compiled in and the sensor reads out inverted. ro.camera.sensor_orientation cannot correct it. Patch 0014 makes the vertical flip unconditional, and is photo-confirmed on both crown and checkers: upright picture, correct color, no calibration needed; see docs/INSTALL.md step 3.

OV9734 devices should also take patches/0015 and patches/0016 for the privacy latch. On crown and checkers the button Android reports as the power key is physically the privacy mute button (linux,code = <116> on the amazon-gating node), so a long press both opens the power menu and engages the hardware latch that cuts camera power. The latch survives a reboot and software cannot clear it, and the camera driver never reads it, so it powers the sensor into a dead rail: the camera stays broken until the device is unplugged from mains. 0015 makes the driver read the latch and refuse the open cleanly; 0016 keeps the camera enumerated while the latch is engaged, so clearing the shutter restores it without a cameraserver restart or a reboot. Both are validated on crown. See issue #4.

Prerequisites

  • An Echo Show 5 or 8 already running R0rt1z2's unofficial LineageOS 18.1 port from amazon-oss/releases - amonet-unlocked, with TWRP intact. If you cannot boot TWRP, stop: some of these steps can brick a device that has no recovery path.

  • amonet 2.0.1 or newer. This is required, not a recommendation. Everything here is written for and tested on 2.x only; amonet 1.x is retired and its instructions have been removed. scripts/flash-boot.sh detects a 1.x device and refuses to write to it.

    Upgrade if you have not. Two reasons:

    The device gets its full 2 GB of RAM. amonet 1.x left these devices running on 1 GB; 2.x gives you all of it. Measured on crown after the upgrade, MemTotal is 1933452 kB (the remainder of the 2 GB is reserved carveout). On a device this size that is the difference between a usable system and one that thrashes, and it is reason enough on its own.

    The boot layout changed and the two are mutually unbootable. 1.x kept an exploit header in the first two blocks of the boot partition; 2.x removed the microloader entirely and boot became an ordinary Android boot image. Writing either layout onto the other generation hangs the device at the vendor logo.

    Do not use amonet 2.0.0: it has a bug that leaves TWRP not properly updated when upgrading from a 1.x unlock, which is exactly the path you are on. 2.0.1 fixes it.

  • A Linux host with adb and python3, and a USB cable. USB is what matters: TWRP has no networking, so the flashing steps need it. Network adb (adb connect <ip>:5555) is convenient for the rest but optional.

  • A LineageOS 18.1 build environment (needed to build the patched boot image and the patched system libraries). Set up the source tree per the amazon-oss instructions; patches/local-manifest-fixes.xml contains the manifest fixes this work needed.

Installation

docs/INSTALL.md is the complete step-by-step guide - every command from a stock LineageOS install to a working, color-calibrated camera, plus troubleshooting. The overview below is the map; the guide is the route.

There are three layers. They must all be applied; each fixes failures the next layer would otherwise hit.

  1. Kernel (boot image): Amazon struct layouts, the OV02B10/OV9734 driver, sensor timing fixes. Requires building and flashing a boot image.
  2. ROM system libraries (device tree + AOSP patches): camera provider declaration, front-camera feature declaration, sensor orientation, two AOSP camera fixes. Requires building the ROM (or at minimum the affected libraries) with the patches applied.
  3. Vendor blobs and on-device tuning: the proprietary camera stack (fetched from a public firmware dump, never shipped here) and the patched private display framework go into the vendor tree before the build; the LD_PRELOAD shim and the two tuning corrections are applied to the device afterwards, because they are built against, or patch, what is on the device.

Layer 1: kernel

Three things go into kernel/amazon/mt8163-4.9, in order:

  1. patches/0001-imgsensor-use-the-Amazon-struct-layouts-on-all-echo-show.patch (every Echo Show device)
  2. patches/ov02b10_mipi_raw-driver.tar.gz unpacked into drivers/misc/mediatek/imgsensor/src/mt8163/ - the complete OV02B10 driver, with all its fixes already included (cronos only)
  3. patches/0004-imgsensor-ov02b10-driver-support.patch - sensor IDs, sensor list entry, defconfig (cronos only)

Do not additionally apply 0007/0008/0010 (already inside the driver tarball; they document how the fixes were developed), do not apply 0002 (a conflicting alternative to 0001), and do not edit the defconfig by hand - 0004 sets the verified configuration. 0003 is debug logging only.

Build the boot image (mka bootimage), then flash it with scripts/flash-boot.sh <adb-serial>.

On amonet 2.x the boot partition is an ordinary Android boot image, so the build's boot.img is written to it verbatim. The flash script backs the partition up first, verifies the transfer by hash, and reads the partition back afterwards. It also checks the recovery partition and refuses outright if the device is still on 1.x, whose layout is mutually unbootable with this one.

If the device is not up far enough for adb, fastboot does the same job with no booted system and no root. fastbrick leaves the device there:

fastboot flash boot out/target/product/<device>/boot.img
fastboot reboot

Note the argument order: fastboot flash <partition> <file>. Omitting the partition fails with "unknown partition".

Backups under backups/ predating the 2.x upgrade are unbootable. They are full partition dumps, so anything captured on 1.x carries the old exploit header and will hang the device at the vendor logo. Three devices were bricked on boot layout, the last one exactly this way. After changing the unlock, reflash from the build tree, never from an older backup.

Layer 2: ROM changes

Apply:

patches/0005-camera-device-1.0-cookie-fallback-and-ANativeWindowBuffer-preview.patch
patches/0006-camera-flatten-tolerate-zero-size-String8-from-legacy-HALs.patch
patches/0011-device-tree-camera-enablement.patch

0005 and 0006 patch AOSP camera code the tree builds (camera.device@1.0-impl, libcamera_client); 0011 carries all the device tree changes (provider declaration, packages, front-camera feature, sensor orientation, HAL1 native handle flag). patches/README.md explains what each change is for.

Not camera related but required on crown and cronos: patches/0017-device-tree-bluetooth-enablement.patch fixes Bluetooth, which is otherwise completely broken there (the adapter crash-loops on enable and BLE scans fail). Tested and verified on both; whether checkers needs it too is not yet known. The patch header carries the analysis; docs/INSTALL.md step 4 applies it with the others.

The blob list also has to be declared and the vendor makefiles regenerated (device/amazon/<device>/setup-makefiles.sh) - appending to proprietary-files.txt alone does nothing, because the build reads the generated vendor/amazon/<device>/*.mk.

Layer 3: vendor blobs, then on-device tuning

Into the vendor tree, before building the ROM:

scripts/fetch-camera-blobs.sh                    # 45 blobs from the public dump
scripts/install-blobs-to-tree.sh <tree>          # place them at their declared paths
scripts/install-private-dpframework.sh <tree>    # API 25 display framework: private
                                                 #   soname + 3 binary patches
scripts/patch-shim-needed.sh <tree>              # add the shim to the blobs' DT_NEEDED

Onto the device, after flashing the ROM:

shims/libcmdqevent/build.sh <serial>         # built against the device's own bionic
scripts/install-cmdq-event-shim.sh <serial>  # cmdq event translation + AWB correction
scripts/patch-awb-d65.sh <serial>            # neutralize the double white balance
scripts/patch-obc-pedestal.sh <serial>       # correct the black level for the OV02B10
adb -s <serial> reboot

scripts/install-camera.sh <serial> <tree> pushes the vendor-tree blobs, provider libraries, shim and manifest directly, for iterating without a full reflash.

The blobs come from the public amazon_cronos_dump firmware dump. This repository contains no proprietary code; the scripts fetch, patch, and install it on your own device.

Verify

From a cold boot, with no manual steps:

adb shell 'pm list features | grep camera'   # camera.any + camera.front, NO plain "camera"
adb shell 'dumpsys media.camera | grep Orientation'   # 0
adb shell 'logcat -d | grep -cE "startStream fail|deque DISPO fail"'   # 0

Open the camera app: live preview, then take a photo - it should produce a full resolution 1600x1200 JPEG in about a second. In a fully dark room a photo should be essentially black (that is the black-level fix; without it you get a grey-cyan haze).

Color calibration

The MediaTek tuning in the blobs is for the OV9734 sensor, not the OV02B10, so adaptive white balance lands with an illuminant-dependent bias. The shim corrects it by rescaling the AWB algorithm's output, interpolated between two calibrated anchors (daylight and warm LED), keyed on the algorithm's own blue gain. Four 512-based properties control it, re-read every 64 frames so tuning needs no restart:

persist.camera.awbtrim.r        cool (daylight) anchor, red
persist.camera.awbtrim.b        cool anchor, blue
persist.camera.awbtrim.r.warm   warm (2600K) anchor, red
persist.camera.awbtrim.b.warm   warm anchor, blue

Defaults are in shims/libcmdqevent/camera-bringup.rc, calibrated against a grey surface on one device. To touch up for your unit: point the camera at anything grey or white, take snapshots, and adjust in steps of 2 or 3 (the color matrix amplifies a change here several times over in the rendered image, so small steps):

  • image too magenta: lower .r (or .r.warm under warm light)
  • image too green: raise it
  • same logic for blue with .b / .b.warm

Known limitations

  • Scene-to-scene color variance of roughly +-10%. The blob's AWB output wobbles a few percent between sessions on identical scenes and the color matrix amplifies it. Inherent to the closed algorithm.
  • Dim mixed lighting can render greenish. When the AWB classifier finds no recognizable illuminant it falls back to its reference gains, which bypass part of the correction. A handler for this state is planned.
  • Low-sun color gradient on aged units. Direct low sunlight through the front glass produces a cyan-to-magenta gradient across the frame (IR leakage through the aged IR-cut filter plus internal veiling glare). It is not visible in midday diffuse daylight or under artificial light. No global correction can fix it, and the front glass cannot be cleaned from outside.
  • Single client. The legacy HAL1 stack allows one camera client at a time.

Safety rules

Learned the hard way; both of these can take the device down completely.

  1. Never SIGKILL or stop cameraserver while the ISP is streaming. It leaves the memory management unit pointed at freed buffers and the resulting bus violation storm livelocks the entire device - no adb, no ping, no watchdog. Use adb reboot, or am force-stop the camera app first and give it two seconds.
  2. Only flash boot images with scripts/flash-boot.sh or fastboot flash boot. Both take the build's plain boot.img. The script additionally refuses a 1.x device and verifies the write.
  3. Never restore a boot backup captured before the 2.x upgrade. It is a full partition dump in the retired 1.x layout and will not boot. Reflash from the build tree instead.

Repository contents

Path What it is
docs/INSTALL.md Step-by-step installation, verification, calibration, troubleshooting
docs/findings.md The full investigation, in order, with every dead end kept and marked
docs/building.md Getting the LineageOS tree for these devices to actually build
docs/handoff-takepicture.md Worked example of debugging one bug end to end (the capture stall)
patches/ Kernel and AOSP patches, numbered in application order
shims/libcamera_shim/ Source shim closing the 11-symbol gap between the API 25 blobs and Android 11
shims/libcmdqevent/ LD_PRELOAD shim: cmdq event-id translation, AWB output correction, diagnostic tracers
scripts/ Fetching, patching, installing, flashing, calibration
tools/cmdq-trace/ On-device diagnostic tools (ioctl tracers, ISP register/IRQ probes, sensor register poke)

Upstream

This work sits on top of R0rt1z2's LineageOS ports for the MT8163 Amazon devices, which is what makes any of it possible:

Releases (flashable ROMs) amazon-oss/releases
Echo Show 5 2nd gen build lineage-18.1-cronos-v0.3
Manifests for building amazon-oss/local_manifests
Device tree android_device_amazon_cronos
Common device tree android_device_amazon_mt8163-common
Kernel android_kernel_amazon_mt8163
Maintainer R0rt1z2

The patches here are written against those trees. Nothing in this repository replaces the port - it adds the camera to it.

Credit

The crown diagnosis by CesarAmores (writeup) mapped the HAL1 shim crash chain and established that the stock blobs are 2017-era camera1 code. R0rt1z2 maintains the LineageOS ports these devices run (amazon-oss) and documented the original kernel/blob mismatch in releases#5. The stock firmware dump is maintained at el-vertedero/amazon_cronos_dump.

License

Original code (shims, scripts, tools) is MIT. Kernel patches are GPL-2.0; AOSP patches are Apache-2.0. See LICENSE. No proprietary binaries are distributed in this repository.

About

Full camera and bluetooth enablement for LineageOS 18.1 on the Amazon Echo Show 5 (1st/2nd gen) and Echo Show 8 (1st gen). Kernel sensor driver, userspace shims and image tuning corrections.

Topics

Resources

Stars

27 stars

Watchers

3 watching

Forks

Sponsor this project

Contributors

Languages