LineageOS 15.1 for Axon 7 (A2017G): Using Docker to Build from Source
Going through my clutter of gadgets, I came across an old Android ZTE Axon 7 (A2017G) phone that was running Nougat. It’s been lying around for a while, and I was looking for (as usual) an excuse to kick off another one of my tech-discovery projects.
I was reading up on LineageOS — a fork of CyanogenMod. CyanogenMod’s roots originate from the Android Open Source Platform (AOSP).
I could just download an existing Lineage build zip and flash it onto the Axon…but that’s no fun.
What was more of an appealing option, was building the OS from source using base LineageOS 15.1 repo and the official ZTE Axon 7 git device-specific source branch (LineageOS 15.1 Axon Tree). There’d more satisfaction from installing a build I’ve generated vs somebody else’. It would also be a stepping stone into the sphere of Custom ROMs and Android tweaking.
Going the extra mile, I wanted to throw docker into the equation, see if I could come up with a portable docker “template” to support alternate Android builds.
There were a few reasons for choosing LineageOS, and this particular version (v15.1).
The first of these was that version 15.1 is based on Android Oreo, where the system-as-root partitioning had yet to be introduced. One other reason, was that Lineage 15.1 was an already bedded down code-base for this device. Versions beyond 15.1, were not listed for the Axon at https://wiki.lineageos.org.
This article is an exploration piece which aims to:
- Go through the process of building LineageOS15.1 (an Android Oreo 8.1.x based ROM) for the Axon 7 A2017G using a docker container derived from a docker image I built to support this process.
- Touch on topic Qualcomm Snapdragon 820 MSM8996 requirements for unlocking the bootloader and enabling Fastboot for certain firmware variants.
- Outline the procedure for installing/flashing the build onto the physical device.
Axon 7 (A2017G) — Key Hardware Details
Here are some of the key hardware details for the Axon 7 (A2017G) variant:
- Chipset/SoC: Qualcomm Snapdragon 820 MSM8996
- CPU: 2x Dual Core Kryo (total of 4 cores)
- Architecture: 64 bit armv8-a
- GPU: Qualcomm Adreno 530
- Built in Storage Type: UFS 2.0
- RAM: 4GB LPDDR4
Further hardware details can be sourced from here.
Host Environment Requirements
Using a Linux host running docker is preferable, and likely to give a better chance of successfully building LineageOS15.1 using the docker image.
The whole process was tested on a VMWare Ubuntu 19.10 VM. I refer to this as the Host throughout the article.
The recommended amount of RAM for the build process is 16GB, and ~ 200GB of free space.
The code sync itself takes up around 65GB of space, and after factoring in the output from the build components, you’re looking at around ~160GB.
Build and Configure Docker Image
The components required to build the docker image can be found within the following Git repo. The README.md within the repo, contains further details on usage/components.
Clone Docker Repo onto Host Machine
The commands below clone the repo to target: $HOME/docker_repo/dev-docker-android
.
$ cd $HOME
$ mkdir docker_repo
$ cd $HOME/docker_repo/
$ git clone \
https://github.com/tonys-code-base/dev-docker-android.git
Edit Local Repo gitconfig Details
A git identity is required for cloning the LineageOS15.1 repo source. You can configure the Dockerfile to reflect your preferred git identity. The existing docker image treats the git
profile below as "system wide" within containers derived from the image (i.e. target location within the container/image at/etc/gitconfig
).
Edit the local repo file and update to reflect your git
identity:
gitconfig: $HOME/docker_repo/dev-docker-android/gitconfig
[user]
name = Your Name
email = me@example.com
Building the Docker Image
Run the following to create the image android-dev
:
$ cd $HOME/docker_repo/dev-docker-android
$ docker build --build-arg userid=$(id -u) \
--build-arg groupid=$(id -g) \
--build-arg username=$(id -un) -t android-dev .
Create Host Directories for LineageOS 15.1 Build
Lineage Project Location
Just about all of the activities involved in the build process will be performed from within a docker container. To ensure the project components are persisted on the host (and not purged for cases such as container/image pruning), a common host:container directory should be established. This directory can later be mounted when running the docker image.
For this article, the location of this directory on the host machine is assumed to be at:
$ mkdir $HOME/lineage15
Ad-hoc Files Location
A similar setup to that of the above is recommended for accessing/sharing non-specific build/project components. This is assumed to be:
$ mkdir $HOME/lineage_dloads
Run Docker Container
- We spin up a container using our image as follows:
$ sudo docker run -it --rm --privileged \
--cap-add=ALL \
-v $HOME/lineage15:/lineage15 \
-v $HOME/lineage_dloads:/lineage_dloads \
android-dev
- This creates a container with the following host:container directory mappings:
|-------------------------------------------|
| host | container |
-----------------------|--------------------|
| $HOME/lineage15 | /lineage15 |
| $HOME/lineage_dloads | /lineage_dloads |
--------------------------------------------|
Initialise and Sync LineageOS15.1 Repo
Initialise
- Change into our project directory. This will be the top level of our build (in Android’s official documentation, it is often referred to as
$ANDROID_BUILD_TOP
).
$ cd /lineage15
- Run
repo init
, to initialise the repository. The value oflineage-15.1
supplied for parameter-b
, ensures we are initialising for the correct Lineage source branch.
$ repo init -u https://github.com/LineageOS/android.git -b \
lineage-15.1
You will be requested to confirm your git identify, which should match the details supplied in your docker gitconfig
file. Respond as you see fit for repository appearance preferences.
Sync
- To download/sync local repo from the remote, run the following:
$ cd /lineage15
$ repo sync
- Take a long break. There is around 65GB of code to download.
One the download is complete, there are some slight modifications to make before continuing.
Add Axon 7 Device to the Build
By now your top level project folder, $ANDROID_BUILD_TOP
(/lineage15
) should contain the Lineage15.1 code branch. We still need to fetch the device specific repo.
Fix Device Combo Menu Choice
The file /lineage15/vendor/lineage/vendorsetup.sh
that was fetched from the 15.1 base repo contains a reference to a remote file path (https://raw.githubusercontent.com/LineageOS/hudson/master/lineage-build-targets
) from which device target build details are sourced. This remote file no longer has an entry for Lineage15.1, so for the purposes of consistency, we can replace contents of existing file /lineage15/vendor/lineage/vendorsetup.sh
to point to /lineage-build-targets.txt
:
for combo in $(cat /lineage-build-targets.txt | sed -e 's/#.*$//' | grep lineage-15.1 | awk '{printf "lineage_%s-%s\n", $1, $2}')
do
add_lunch_combo $combo
done
/lineage-build-targets.txt
was included as part of the docker image build and contains the the entry for our Axon-specific Lineage15.1 build.
Product/Device Specific Code
We need to ensure that we have all the appropriate make/config files & their dependencies. These include a directory structure for our device configuration ($ANDROID_BUILD_TOP/device/<company-name>/<device-name>
).
Luckily, the folks at over at Axon 7 LineageOS Wiki have already included these in the Axon-specific repo. If you’re interested in further details configuring a product/device, refer to the Android documentation.
The makefiles (.mk
) for the Axon are fetched/generated as part of the scripts mentioned in the quote below. The response to option we provide to the breakfast
command (later on) fetches device-specific code from github.
Note: When script
/lineage15/build/envsetup.sh
is invoked, it calls/includes the script at/lineage15/vendor/lineage/build/envsetup.sh
As of now, an ls
on /lineage15/device
shows the following:
/lineage15/device
├── common
├── generic
├── google
├── lineage
├── qcom
└── sample
Let’s run the necessary commands/scripts to generate/fetch the Axon 7 specific code into <company-name>/<device-name>
above:
- Start by activating our environment:
$ cd /lineage15
$ source build/envsetup.sh
- The
envsetup.sh
imports many functions, commands and environment variables that are required for the build. - To choose the product we wish to build for:
$ breakfast
breakfast
generates the following output:
including vendor/lineage/vendorsetup.sh
You're building on Linux
Lunch menu... pick a combo:
1. full-eng
2. lineage_axon7-userdebug
Which would you like? [aosp_arm-eng]
- Choose option
2
to generate/fetch the Axon 7 specific code. - Once done, part of the output log from
breakfast
is shown below. You will notice in the output that the Axon 7 specific code is retrieved from remote repo https://github.com/LineageOS/android_device_zte_axon7, and the branch being checked out islineage-15.1
.
build/core/product_config.mk:238: *** Can not locate config makefile for product "lineage_axon7". Stop.
Device axon7 not found. Attempting to retrieve device repository from LineageOS Github (http://github.com/LineageOS).
Found repository: android_device_zte_axon7
Default revision: lineage-15.1
Checking branch info
Checking if device/zte/axon7 is fetched from android_device_zte_axon7
...
Using default branch for android_device_zte_axon7
Syncing repository to retrieve project.
...
repo sync has finished successfully.
Repository synced!
Looking for dependencies in device/zte/axon7
Adding dependencies to manifest
Checking if device/qcom/common is fetched from android_device_qcom_common
Adding dependency: LineageOS/android_device_qcom_common -> device/qcom/common
...
- Once complete, the final output provides a summary of the target we’re building
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=8.1.0
LINEAGE_VERSION=15.1-20200629-UNOFFICIAL-axon7
TARGET_PRODUCT=lineage_axon7
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_PLATFORM_VERSION=OPM1
TARGET_BUILD_APPS=
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=kryo
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=kryo
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-5.3.0-61-generic-x86_64-with-Ubuntu-19.10-eoan
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=OPM7.181205.001
OUT_DIR=/lineage15/out
AUX_OS_VARIANT_LIST=
============================================
- You will also notice that path
/lineage15/device/<company-name>/<device-name>
has been created at:
/lineage15/device/zte
└── axon7
and that some .mk
files exist:
$ ls /lineage15/device/zte/axon7/*.mk
Android.mk BoardConfig.mk device.mk lineage.mk
Extracting Proprietary Blobs
Proprietary blobs are files/modules that are required for the build to successfully complete.
If you are already running Lineage on your Axon 7, then you can use your device to extract the blobs.
If you do not have Lineage running on the device, you will need to have access to a flashable zip from which you can extract the blobs.
Procedures for each of the above scenarios is described below.
Extracting from Axon 7 Device with LineageOS15.1 Installed
To extract blobs from an Axon 7 with LineageOS already installed and running:
- Exit the docker container.
- Attach your Axon 7 to your host via USB.
- Ensure that the
ADB Debug Bridge
is enabled via your device settings:Developer Options -> Debugging -> Android Debugging
. - Run the following to bring up the docker container:
$ sudo docker run -it --rm --privileged \
--cap-add=ALL \
-v $HOME/lineage15:/lineage15 \
-v $HOME/lineage_dloads:/lineage_dloads \
android-dev
- Once at a prompt within the container, change directory to top level project folder and activate the environment:
$ cd /lineage15
$ source build/envsetup.sh
- Change directory so that we are in the Axon 7 device tree:
$ cd /lineage15/device/zte/axon7/
- Execute the following script to pull the blobs from your device:
$ ./extract-files.sh
Extracting from LineageOS15.1 Flashable Zip
The method that will be described is based on a flashable zip that is classified as Blocked Based
:
Block-based OTA: the content of the system partition is stored inside of an
.dat
/.dat.br
file as binary data.
…If your zip has no
system
folder or it is nearly empty and a file namedsystem.transfer.list
exists at the root level, then what you have is a block-based OTA
…
- Download lineage-15.1–20200622-UNOFFICIAL-axon7.zip and save on the host at location
$HOME/lineage_dloads
, remembering that this host directory is mapped/mounted inside the container at/lineage_dloads
. - From within the docker container, check to ensure this in fact is a Block-based zip by checking that the
system*
suffixes aredat/dat.br
, thesystem
folder is nearly empty and thatsystem.transfer.list
exists:
$ cd /lineage_dloads
$ unzip -l lineage-15.1-20200622-UNOFFICIAL-axon7.zip | \
grep -i -e system -e vendor
...
system.new.dat.br
system.transfer.list
system/build.prop
...
vendor.new.dat.br
vendor.transfer.list
...
- We also have a similar scenario for the
vendor*
files, and they will also need to be included when extracting the blobs. - Create working directory for the extract process:
$ cd /lineage15
$ mkdir system_dump
$ cd system_dump
- Unzip the required
system*
&vendor*
files:
$ unzip /lineage_dloads/lineage-15.1-20200622-UNOFFICIAL-axon7.zip system.transfer.list system.new.dat*
$ unzip /lineage_dloads/lineage-15.1-20200622-UNOFFICIAL-axon7.zip vendor.transfer.list vendor.new.dat*
- Decompress the output files. This method uses
brothli
, which is already include as part of the Dockerfile:
$ brotli --decompress --output=system.new.dat system.new.dat.br
$ brotli --decompress --output=vendor.new.dat vendor.new.dat.br
- Convert into mountable
.img
files:
$ python /usr/local/bin/sdat2img.py system.transfer.list system.new.dat system.img
$ python /usr/local/bin/sdat2img.py vendor.transfer.list vendor.new.dat vendor.img
- Note, the script used (
sdat2img.py
) for converting the vendor/system.new.dat
files to mountable images, was originally sourced from this git repo. - Create mount point and mount
system.img
:
$ mkdir system
$ sudo mount system.img system/
- Remove existing symbolic link on the mounted image and create mount point for our
vendor.img
, then mount it:
$ sudo rm system/vendor
$ sudo mkdir system/vendor
$ sudo mount vendor.img system/vendor/
- Change to the location containing
extract-files.sh
and execute the script, passing in the location from which the blobs are to be extracted (system_dump/
)
$ cd /lineage15/device/zte/axon7
$ sudo ./extract-files.sh /lineage15/system_dump/
- Unmount images and remove the
system_dump
directory:
$ sudo umount /lineage15/system_dump/system/vendor/
$ sudo umount /lineage15/system_dump/system/
$ rm -fr /lineage15/system_dump
Build LineageOS15.1
- Kick off the build by running:
$ cd /lineage15
$ brunch axon7
- The build can take up to several hours and should be monitored intermittently for errors.
- Once the build has completed successfully, the output should display the location and name of the
LineageOS15.1
flashable zip produced by the build:
Package Complete:
/lineage15/out/target/product/axon7/lineage-15.1-<YYYYMMDD>-UNOFFICIAL-axon7.zip
#### build completed successfully (04:42:23 (hh:mm:ss)) ####
The file has the following naming convention : lineage-15.1-<YYYYMMDD>-UNOFFICIAL-axon7.zip
.
Axon 7 A2017G — State Prior to Flashing LineageOS15.1
Note the instructions below were tested on an Axon 7, A2017G variant. The testing was carried out on a device that was in the following state prior to flashing the build onto the device:
- Android Version —
Nougat 7.1.1
- Build Number —
ZTE A2017GV1.2.0B10
- Bootloader —
LOCKED
- SanDisk 32GB SD Card for storing flash files
Unlock Bootloader & Enable Fastboot
NOTE: The procedure that follows will perform a FORMAT/WIPE of all user and system data. Make a FULL backup before continuing.
Before commencing, ensure you have enabled ADB Debugging on your phone and that OEM Unlocking
is enabled in your phone Developer Options - Advanced Settings
.
Reboot into the bootloader
using:
$ adb reboot bootloader
- If the above command worked successfully, you should see the
bootloader
screen appear on your phone. If so, the below command's output should list your device:
$ fastboot devices -l
...
488f9532 fastboot
- Run the following to unlock the bootloader:
$ fastboot oem unlock
- Your phone screen should then ask you to confirm the details, using your phone’s
Volume Up/Down
andPower
buttons.
If you’ve gotten this far, you can skip to the section “Install Custom Recovery (TWRP)”.
Unable to Boot into Bootloader/Fastboot
If you still can’t boot into the bootloader
, then it's likely that updates to partitions residing on the Qualcomm chipset embedded storage (aka UFS), would need to be updated. Generally, the partitions affected are named aboot
and/or fbop
. This method requires booting into Emergency Download Mode (EDL), and flashing these partitions using a proprietary programmer, which utilises the firehose
protocol to access the Snapdragon 820 MSM8996 SoC flash memory. These programmers have made their way onto various online sites and come packaged in toolkits for unlocking, as well as full EDL image downloads.
Below are the physical partitions and files names which are manipulated/overwritten by the various toolkits.
filename="emmc_appsboot.mbn" label="aboot"
filename="fastboot.img" label="fbop"
This is a topic for another discussion, but I’d suggest you head over to XDA and familiarise yourself with topics/threads related to bootloader unlocking. You will find references to “XiaoMiFlash/MiFlash” and several “Toolkits” to help with the unlocking of the bootloader.
Install Custom Recovery (TWRP)
In order to be able to prepare the device for LineageOS15.1, we’ll need a custom recovery.
- Head over to https://twrp.me/zte/zteaxon7.html and download the latest TWRP
.img
recovery file for the Axon. - Ensure you are in
fastboot
mode, if you are not, then runadb reboot bootloader
to reboot the device intofastboot
mode. - Run the following to ensure your device is listed:
$ fastboot devices -l
...
488f9532 fastboot
- Flash the TWRP recovery
.img
using the following (the below assumes animg
file namedtwrp-3.3.1-0-ailsa_ii.img
):
$ fastboot flash recovery twrp-3.3.1-0-ailsa_ii.img
Once the flash is complete you will need to boot into Recovery Mode, otherwise the default
recovery.img
for the stock ROM will load.
- Boot into TWRP recovery by using the phone’s “Volume Up/Down” to toggle between the options near the top of the phone’s screen until you see “Recovery Mode”, then press “Power” button to trigger a boot into TWRP.
- From TWRP, perform a “Wipe”
-->
"Advanced Wipe" and tick "Dalvik/ART Cache", "Data", "Cache" and "System". Perform a "format data", if you had encrypted storage. - You will need to stay in
recovery mode
for the next section ("Installing a Universal Bootstack") - If you accidentally exit
recovery mode
boot back into recovery as follows: a) Long pressingPower
to switch off the device.
b) Once the device has switched off, simultaneously hold downPower + Volume Up
.
c) When theZTE
logo appears, release thePower Button
and continue holding the.Volume Up
.
d) Once the TWRP recovery screen appears, release theVolume Up
button.
Install Universal Oreo Bootstack, Modem and LineageOS15.1
Before we can flash our build of LineageOS15.1, we will need a compatible bootstack (elements of the firmware, i.e. in this case, the phone’s modem
and bootloader
) that will support loading LineageOS15.1.
You can download a Universal Oreo Bootstack and modem as described in the following XDA thread.
I downloaded the following from links posted in the above thread:
Bootstack
- Filename:
A2017x_LineageOS15.1_UniversalBootstack_v2_by_DrakenFX.zip
- Link: https://androidfilehost.com/?fid=11410932744536985259
- SHA256 Digest:
39119585B6B04BD7D227F34BC8E631852435F33DE085DE376230543026346F41
Modem (for A2017G)
- Filename:
A2017G_OreoModem.zip
- Link: https://androidfilehost.com/?fid=962339331458992302
- SHA256 Digest:
03F7C22EAC86AC3A6B1A77C3CFFF0EF7CE3DD4D5DDE45389DB0D4D5240DD6125
Save the above files, along with the flashable zip from your build’s output (lineage-15.1-<YYYYMMDD>-UNOFFICIAL-axon7.zip
), onto the SD Card (or internal storage). Your Axon should be accessible as a USB mass storage device via the host machine once you're in TWRP recovery.
Go back to the TWRP main menu to start installing/flashing the files:
- Choose
install
and navigate to location of the bootstack zip file (A2017x_LineageOS15.1_UniversalBootstack_v2_by_DrakenFX.zip
). - Untick “Reboot after installation is compete” and swipe to flash.
- Repeat the above steps for the
A2017G_OreoModem.zip
file. - Now reboot into TWRP recovery again with the new
bootstack/modem
. TWRP has an option to select booting back into recovery. During the reboot, accept any prompt/warning relating to corrupt/unlocked bootloader by pressing the "Power" button. - Once back in TWRP, you can finally flash your
lineage-15.1-<YYYYMMDD>-UNOFFICIAL-axon7.zip
. - Perform a Wipe cache/Dalvik.
- Reboot the system, accept bootloader prompts regarding “unlocked/corrupt” bootloader by pressing the “Power” button.
- Give the initial boot a few minutes to load. If you don’t see the LineageOS splash screen after a few minutes, power off the device by long pressing Power key and let it reboot once more.
Hopefully by now, you’ve made it to the Lineage welcome screen where you can begin your device configuration.
After you’re done, you might notice that the details in “About Phone” in your System settings, lists the device as “A2017U” and not “A2017G”. This may be due to the options configured for the default build. The phone should still function as expected.
You should be able to see your build details listed in “About phone”.
The LineageOS version shown, is the same as that generated by our breakfast
summary, i.e :
LINEAGE_VERSION=15.1-20200629-UNOFFICIAL-axon7
Final Points
If you’ve stumbled on this article, I hope you’ve found it to be informative. If you come across inaccurate details, feel free to comment and I will update as required.
Note that Google apps/services were not included in the default build. Refer to this link for further details on installing. If you intend on installing these, then I’d suggest they be flashed from TWRP, immediately following the step described for flashing your lineage-15.1-<YYYYMMDD>-UNOFFICIAL-axon7.zip
.
The docker repo that was specifically setup for the discovery, could be used for other Android builds. Depending on your specific build requirement, modifications to the existing Dockerfile may be required.