Process for building and installing your own Android Open Source Project (AOSP) OS with a locked bootloaderNew: Updated for Android 11!Rationale

It is very difficult to find a modern cell phone that simultaneously gives you privacy and ownership, without sacrificing a reasonable level of security. Apple products give you decent security and (presumably) privacy, but not ownership (ability to root and control). Android devices nearly all force on you the privacy-intrusive Google Apps, and mostly lock you out of ownership by preventing rooting and not having an unlockable bootloader.

Ironically some of the most suitable devices for maintaining privacy and ownership along with security are from Google itself, the Nexus and Pixel product lines. There has long been a community around the former Cyanogenmod OS, now LineageOS and similar Android rebuilds. And these builds provided a privacy mechanism along with rootability and reasonable security. Unfortunately these rebuilds now lack decent security because they break the Android security model by leaving the bootloader unlocked.

In the past -- up through the Nexus 5X/Nexus 6P generation of phones -- you could install Cyanogenmod/LineageOS without Google Play and Google Apps, use the open-source F-Droid app store, and the Firefox web browser. To ensure reasonable security, your could give your rooted phone a nice long decryption password (different from the unlock code). The phone would then require this code at bootup to decrypt your files, so a least if you lost the phone and it got turned off or rebooted, the chance of the code being guessed would be minimal. Unfortunately with the current Android security model, the encryption key is kept on the phone (admittedly in a secure processor zone) and is potentially vulnerable to (for example) a different OS being installed underneath it -- which is possible if the bootloader is left unlocked. Now all your data is a lot more vulnerable in the event of a lost or stolen phone, especially if you don't use an extremely long unlock code.

The obvious solution is to re-lock the bootloader after installing your rooted, alternative OS. While re-locking the bootloader was once considered dangerous (risk of bricking your phone) this is no longer the case on phones designed to have unlockable bootloaders, as even if the OS fails completely you can just re-unlock the bootloader to replace the OS. Unlocking the bootloader will then wipe your data (that's the protection of your data in case the device is stolen) but your phone will still be workable. Unfortunately you can't meaningfully lock the bootloader unless your OS is signed -- which most of the best known alternative OS's aren't (A few, such as GrapheneOS or the are designed to support re-locking the bootloader)

Android "security" "features" and their merits Note: This is to my best understanding
FeatureMeritsDownsideDesirable in our context
EncryptionProtects data from disclosure if phone is lost/stolenMost effective if bootloader is lockedYes
Non-rootableGives corporate media companies a warm, fuzzy feeling that they, not you, control your phoneLoss of phone control; more difficult to troubleshoot/tweakProbably not
Non-unlockable bootloader (not possible to enable OEM unlocking)Protects corporate power; may prevent you from messing up or bricking your phone; may make phone less valuable if stolenCan not root or install custom OSNo (showstopper problem)
OEM unlocking not enabledPrevents wiping/factory reset without device passcode -- makes phone less valuable if stolenIncreased chance of bricking due to a bad updateProbably not
Locked bootloaderPrevents unauthorized (not properly signed) updates that may bypass securityOS and updates must be properly signed prior to installationNot during initial installation but after that, yes
Rollback protectionPrevents downgrades to previous versions that may have exploitable security flawsRecovering from a failed OS update may require wiping the deviceNo, so long as all signed previous upgrade images are kept secure and are unavailable to an attacker
This process: Encrypted, optionally rootable, locked bootloader, optional rollback protection.

This procedure is designed to give you a secure and private phone. It is intended to work on the Pixel (2 and above) series of phones from Google, but has only been tested on a pixel 5. The procedure includes unlocking the bootloader, compiling your new operating system, generating signing keys, installing your new OS, relocking the bootloader, and performing/installing updates.

Be aware the the OS you compile will not include Google Apps or the Google Play app store. You can install the F-Droid Open-source app store. You will still be able to access Google services via a web browser such as Firefox. If you use android_prepare_vendor with the "full" option it may be possible to install an aftermarket Google Apps distribution or a clone such as opengapps, but I have not tested this.

An alternative is to download Graphene OS. It is an open-source security-focused Android distribution. It's a bit different from base AOSP and has additional security features and isn't rootable.

DO NOT USE THIS WITH THE VERIZON EDITION PIXEL PHONES. THEY ARE NOT DESIGNED TO HAVE AN UNLOCKABLE BOOTLOADER. IF YOU SUCCEED IN UNLOCKING THE BOOTLOADER OF SUCH A PHONE AND RE-LOCK IT, YOU MAY NOT BE ABLE TO UNLOCK IT AGAIN! (The non-Verizon phones work fine with Verizon, and I have tested my Pixel 5 with this process using android_prepare_vendor with the "full" option and it works perfectly!).

FOLLOW THESE INSTRUCTIONS AT YOUR OWN RISK. WHILE THESE STEPS SHOULDN'T BRICK YOUR PHONE, MANIPULATING BOOTLOADERS AND OPERATING SYSTEMS AT THIS LEVEL DOES CARRY A SMALL DEGREE OF RISK. UNLOCKING/RELOCKING THE BOOTLOADER AND INSTALLING THE OS WILL WIPE ALL YOUR DATA FROM THE DEVICE.

Required OS and Android Tools You will need a suitable Linux OS on a suitable computer and the latest Android SDK Platform Tools
  1. Fedora, Ubuntu, Debian, CentOS all likely suitable. I have tested on recent Fedora (32)
  2. Expect to need at least 350 GB disk space and at least 8GB RAM -- more would be better
  3. Install the prerequisite packages e.g. per https://source.android.com/setup/build/initializing or https://fedoraproject.org/wiki/HOWTO_Setup_Android_Development#Compiling_Android_from_Source or other documentation for your OS distribution
  4. Download the latest Android SDK Platform Tools from https://developer.android.com/studio/releases/platform-tools(be sure to use these latest tools over the ones from your OS distro, which may not be sufficiently up-to-date). Unzip them in your home directory creating $HOME/platform-tools. When updating to a major new Android version it is a good idea to update the platform tools.
Phone prep procedure: Updating to latest OTA image and Enabling developer mode

Update your phone to the latest updates. This can be through an OTA update or by downloading the suitable image from https://developers.google.com/android/ota and following the sideloading instructions.

You also need to enable Developer mode and USB debugging on your phone. See https://www.xda-developers.com/how-to-unlock-bootloader-and-root-the-google-pixel-2-and-pixel-2-xl/ For more details. The steps are as follows:

  1. Enable developer options (tap on the build number in System settings/About phone seven times)
  2. In developer options (under Settings/System/Advanced) enable USB debugging and OEM unlocking (may have to have an internet connection to enable OEM unlocking)
  3. Now you should be able to use adb to connect to the phone (may need to confirm on the touchscreen) from the computer e.g. "adb shell"
  4. When you are ready to install your new OS you will need to reboot into the bootloader, and unlock the bootloader, etc. ("adb reboot bootloader", "fastboot flashing unlock") UNLOCKING THE BOOTLOADER WILL WIPE ALL DATA ON THE DEVICE!
  5. To boot a recent Pixel from power off to the bootloader screen hold down the volume down button and then press and hold the power button along with volume down for 10 seconds
  6. If you have trouble getting adb and fastboot to work from your computer, you may need to run these as root. Test with "adb shell" with the phone booted and "fastboot devices" with the phone showing the bootloader screen.
Downloading AOSP source code
  1. These steps (except for the original configuration of your local repository) will need to be repeated when you need to create an update based on a new build ID/branch
  2. Identify the latest appropriate factory image for your phone from https://developers.google.com/android/images
  3. From the build ID of this factory image, e.g 11.0.0 (RQ1D.201205.012.A1, Dec 2020), and your phone model select the appropriate tag from https://source.android.com/setup/start/build-numbers.html#source-code-tags-and-builds. For example, RQ1D.201205.012.A1 maps to tag android-11.0.0_r24. Sometimes it may take a few days between the factory image being posted and the tag being listed.
  4. I put my source code in $HOME/android_build/. Follow the AOSP download instructions https://source.android.com/setup/build/downloading. The "repo" program generally only needs to be downloaded once. For the "repo init" line use the branch you just selected, e.g. repo init -u https://android.googlesource.com/platform/manifest -b android-11.0.0_r24. You can use "repo sync -c" to initiate the download (the "-c" apparently just downloads the selected branch, minimizing unnecessary downloading). Expect repo sync to take many hours. "repo init" and "repo sync" can be freely re-run when updating to a new AOSP version. Before updating you may want to update repo itself by: cd $HOME/android_build/.repo/ ; git pull
Selecting an AOSP build type AOSP supports three types of builds: "user", "userdebug", and "eng". See https://source.android.com/setup/build/building for more information. In general, "user" will get you a non-rooted/non-rootable device, "userdebug" will get you a rooted or rootable device -- but needs some minor changes (see below) to get better security. "eng" builds are primarily suitable for debugging as they have no security. Pick the build type that corresponds to your goals. Making necessary changes to the AOSP source tree The AOSP source tree consists of a large ensemble of GIT repositories, managed together by the 'repo' tool. The 'repo init' and 'repo sync' commands you gave above downloaded and selected a particular tag on each repository. To use your own customized changes, you can create a branch from the AOSP tag to store your changes. Then when creating an update you can create a new branch and cherry-pick the changes from your old branch:
  • Keep notes indicating which repositories within the AOSP source tree you make custom modifications to, so that you will be able to quickly identify them and merge changes when making an update
  • After running repo init and repo sync if you change into one of the repository directories and run git status it will show "HEAD detached at..." and a commit hash. The commit hash corresponds to the tag (can list with git tag) for the android version you selected.
  • To commit changes to the source tree, starting at the tagged version:
    1. Create and checkout a personal branch for this build of the repository you are modifying:
      git checkout -b my_new_branch_aosp11r24
    2. Make your changes, if you haven't already
    3. List your changed/modified and new (untracked) files with git status and add them to be committed with git add.
    4. Commit your changes with git commit and give a suitable commit message
    5. Make a note of which repository you have modified, the name of your custom branch, and the commit ID's for all of your commits (from git rev-parse HEAD or git rev-parse --short HEAD) for future reference when you do updates.

    Having followed this process, when it comes time to do an update, start with your notes of which repositories you have customized. Then:

    1. Change to each customized repository, then
    2. Create and checkout a personal branch for this new build of the repository, e.g:
      git checkout -b my_new_branch_aosp11r25
    3. Cherry-pick all of your prior commits to this repository, based on your commit ID's (you can find commit ID's after-the-fact with gitk --all), e.g:
      git cherry-pick 37638180d6
    4. You may have to manually merge any files with merge conflicts and if you need to do so, be sure to git commit when finished to complete the merge.
Recommended changes to the AOSP source tree You will almost certainly need to make some custom changes to the ASOP tree. For each, be sure to follow the above process for creating a branch and committing your changes.
  1. Adjusting device configuration (in most cases this will be unnecessary):

    This involves changing the device tree for your device, usually in device/google/<codename>. Codenames include: Pixel 2=walleye, Pixel 2 XL=taimen, Pixel 5=redfin. Many devices have a codename for the device family as well as a codename for the specific device. For example the walley/taimen family is muskie and the redfin family is redbull. In these cases both device and family trees are relevant.

    First identify the device tree and aosp configuration file for your build. For Pixel 2 these are under device/google/muskie; for Pixel 5, both device/google/redfin and device/google/redbull are relevant.
  2. Adding the vendor directory to PRODUCT_SOONG_NAMESPACES (required with latest versions of android-prepare-vendor):

    Edit your device .mk file (such as device/google/redfin/device-redfin.mk) and modify it to add vendor/google-devices/<codename> to PRODUCT_SOONG_NAMESPACES, e.g. PRODUCT_SOONG_NAMESPACES += vendor/google_devices/redfin
  3. Disabling rollback protection (these device configuration changes are not useful initially but may e relevant for creating updates):

    (this step is only necessary when creating an update if you think it may fail and want to be able to re-sideload the previous update. It is not a good idea unless you keep your signed update images secure. Success is not guaranteed but it may help you avoid having to wipe your phone unnecessarily)

    We disable rollback protection by making the BUILD_DATETIME, the PLATFORM_SECURITY_PATCH, and the PLATFORM_SECURITY_PATCH_TIMESTAMP exactly match the values from the prevous build rather than using the defaults from build/core version_defaults.mk and associated files (While your new build will show an old security patch level, it will indeed have all the latest patches).

    To do this, you need to determine the values from your previous (known working) build. Unzip the OTA update from the previous build and look at the META-INF/com/android/metadata file. The post-timestamp value should be your new BUILD_DATETIME; the post-security-patch-level should be your new PLATFORM_SECURITY_PATCH. Determining PLATFORM_SECURITY_PATCH_TIMESTAMP requires running the date command on the value of PLATFORM_SECURITY_PATCH, for example if PLATFORM_SECURITY_PATCH is 2020-04-05, use
    date -d 'TZ="GMT" 2020-04-05' +%s
    
    to determine the correct PLATFORM_SECURITY_PATCH_TIMESTAMP

    Assign these values for your build by creating a vendorsetup.sh file in the device directory (e.g. device/google/muskie/vendorsetup.sh or device/google/redfin/vendorsetup.sh) The file should contain export statements assigning the above variables, for example
    # Disable rollback protection
    export BUILD_DATETIME=1586649600
    export PLATFORM_SECURITY_PATCH=2020-04-05
    export PLATFORM_SECURITY_PATCH_TIMESTAMP=1586044800
    
    You will be able to verify that this process was successful by looking at the META-INF/com/android/metadata file of your final generated OTA update file.

    So long as you do not re-disable OEM unlocking, the worst case if your OTA update fails and the bootloader does not automatically stay at the previous update (it usually will), and rollback protection override fails as well is having to re-unlock the bootloader with fastboot (wiping all data), and install your last known-working generated factory image.

    Don't forget to commit your changes to the device tree to your custom branch, so that you can merge them when you do updates.
  4. Securing userdebug builds:

    userdebug builds by default do not have adb security enabled. I used to adjust this in the device tree, but have found it is simpler and more reliable to adjust within the build system. Specifically, to enable adb security, we need the system property ro.adb.secure=1. If we want root access from adb, we then also need the property service.adb.root=1.

    To do this, modify the build property logic in build/make/core/main.mk. The standard logic is as follows:
    ifneq (,$(user_variant))
      # Target is secure in user builds.
      ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=1
      ADDITIONAL_DEFAULT_PROPERTIES += security.perf_harden=1
    
      ifeq ($(user_variant),user)
        ADDITIONAL_DEFAULT_PROPERTIES += ro.adb.secure=1
      endif
    
      ifeq ($(user_variant),userdebug)
        # Pick up some extra useful tools
        tags_to_install += debug
      else
      ...
    	
    I changed the logic to set ro.adb.secure=1 in both user and userdebug builds, and set service.adb.root=1 in userdebug builds.
    ifneq (,$(user_variant))
      # Target is secure in user builds.
      ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=1
      ADDITIONAL_DEFAULT_PROPERTIES += security.perf_harden=1
    
      ADDITIONAL_DEFAULT_PROPERTIES += ro.adb.secure=1
    
      ifeq ($(user_variant),userdebug)
        # make device rootable via adb with user permission
        service.adb.root=1
    
        # Pick up some extra useful tools
        tags_to_install += debug
    
      else
    
    Don't forget to commit these changes to a private branch, and make note of them as described above so you can merge them into any updates.
  5. Compiling under limited memory:

    The new soong build system by default doesn't allow java build tools a high enough percentage of system memory for building to work with only 8GB (possibly 16GB) of memory. If you are in this situation, you need to modify the soong configuration in build/soong/java/config/config.go. I changed
    	pctx.StaticVariable("JavacHeapSize", "2048M")
    	
    
    to
          pctx.StaticVariable("JavacHeapSize", "8192M")
    
    and
          JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads`
    
    to
          JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads -J-Xmx8192M`
    
  6. Compiling Android 11 on Fedora I also had to modify the $HOME/android_build/build/make repository to explicitly call out python2 for several of the python build tools scripts. This means modifying the header of the .py file to change
    #!/usr/bin/env python
    
    to
    #!/usr/bin/env python2
    
    I had to make this change to the following scripts to prevent errors during the build signing phase:
    • build/make/tools/releasetools/add_img_to_target_files.py
    • build/make/tools/releasetools/check_target_files_signatures.py
    • build/make/tools/releasetools/img_from_target_files.py
    • build/make/tools/releasetools/make_recovery_patch.py
    • build/make/tools/releasetools/ota_from_target_files.py
    • build/make/tools/releasetools/sign_target_files_apks.py
    These changes are only necessary if you get Python errors during build signing, and you do not need to rebuild after making them. When making these changes be sure to create a branch and commit them following the procedure outlined previously. If they are still needed in the next update you can then merge them in.
  7. I also had a problem in AOSP 11 with the developer menu crashing. This problem was fixed by editing frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java, and modifying the isAvailable() method to always return false.
Compiling AOSP source code
  1. Make sure the latest platform tools are downloaded and in your path. and the environment is properly set up:
    	  export PATH=$HOME/platform-tools:$PATH
    	  export LC_ALL=C
    	  export JAVA_TOOL_OPTIONS=-Xmx6g
    	  cd $HOME/android_build
    	  source build/envsetup.sh
    	
  2. Extract the vendor data for the build. There are two options for this:
    1. Download the driver binaries from Google's site https://developers.google.com/android/drivers. See also https://source.android.com/setup/build/building. Make sure you get the version corresponding to your selected build ID. The driver files go into $HOME/android_build/vendor/. The disadvantages of this method are that it won't include functionality required for compatibility with certain carriers (e.g. Verizon), it won't support Google Apps (if you want those), and that it won't support signing the build for a lockable bootloader.
    2. Download and use android-prepare-vendor, which is a set of scripts to extract drivers and some APK's from Google-provided factory images (I have a forked version with a 'full' walleye configuration for Android 11 at https://github.com/sdh4/android-prepare-vendor/tree/sdh4-walleye-android11; for other devices the AOSPAlliance version will generally be the best available).

      WARNING: The execute-all.sh script downloads a bunch of binaries from previous authors of the scripts in its oatdump_deps_download() and then runs them.

      The android-prepare-vendor scripts will automatically download the factory image and OTA update, then extract and fixup the needed files for the AOSP vendor/ tree. execute-all.sh requires a device name (e.g. -d redfin) a build id (e.g. -b RQ1D.201205.012.A1), --full or --naked, and an output path (-o option). I have a big /tmp disk so I usually put the output into /tmp and then copy it into $HOME/android_build/vendor when done. If you want maximum compatibility with carriers (e.g. Verizon) or plan to install Google Apps, you may want the --full parameter in place of the --naked parameter. As of this writing the pull-request for redfin support is still pending so until it is merged you will want to use my fork. Many devices do not contain --full configurations. If you need --full on an unsupported device you will need to provide an api-30 (Android 11) "full" block in the config.json file for your device. You can create this by starting from a previous API or similar device, mounting the Google factory system image, and looking to see if the the referenced bytecode or other files are still present, have moved, or are gone, and then listing them appropriately in the api-30 "full" block.)

      It may be helpful to run execute-all.sh as root (Dangerous!) because that way it can use the kernel loopback module (mount -o loop) rather than somewhat less robust FUSE plugins to mount the factory image.

      My android-prepare-vendor execute-all command (using pre-downloaded factory and OT images stored in /tmp) was:
      execute-all.sh  -d redfin -b RQ1D.201205.012.a1 --full -o /tmp/newvendor -i /tmp/redfin-rq1d.201205.012.a1-factory-c7ccc45f.zip -O /tmp/redfin-ota-rq1d.201205.012.a1-d6bacd68.zip --keep
      	    
      NOTE: It is normal to see the following message:
      [!] Vendor partition contains pre-optimized bytecode - not supported yet
      Not aborting, TODO: FIX properly
      
      Once execute-all.sh has completed successfully, copy the newly generated vendor directory (and vendor_overlay directory , if present) e.g. from /tmp/newvendor/redfin/rq1d.201205.012.a1/vendor/google_devices as $HOME/android_build/vendor/google_devices. If you ran execute-all.sh as root you might need to chown -R the tree to your user ID first.
  3. Do not leave extra trees (built copies, old source trees, etc.) hanging around in the $HOME/android_build directory as certain types of files may be interpreted by the build system and mess with the build. If you need to keep backups or copies, move them outside the source tree
  4. Be sure the environment is properly set up as described above.
  5. For fully a clean build or new version, clean up any existing build output with make clobber. Alternatively if you have made configuration changes and don't want to wait for a full rebuild it may be adequate to to use make installclean before rebuilding for those changes to take effect.
  6. Select your desired build with "lunch" and the target and build type. Build type should usually be "userdebug". Mine was
    	  lunch aosp_redfin-userdebug
    	
    Or if I didn't want my build to be rootable,
    	  lunch aosp_redfin-user
    	
    The environment must be reset and lunch re-run in every new terminal you might use for compiling. To do the build (this command uses 3 parallel processes; if you have lots of memory and CPU cores you can use far more):
    	  make -j3
    	
    The build for me takes 8-10 hours. It generates output in $HOME/android_build/out/target/product/$TARG_DEVICE, where $TARG_DEVICE is your target device, e.g. redfin.
Testing the build (optional, but may want to try it the first time)

See also https://source.android.com/setup/build/running. Also be sure you have unlocked your bootloader (see below) and updated your Radio/bootloader to the same version as your are compiling (flash_base.sh from the factory image)

It generates output in $HOME/android_build/out/target/product/$TARG_DEVICE. Make sure the latest platform-tools are in your path, set ANDROID_PRODUCT_OUT to the directory with the build output, and use fastboot flashall to upload to your device, e.g.

      export PATH=$HOME/platform-tools:$PATH
      export ANDROID_PRODUCT_OUT=$HOME/android_build/out/target/product/$TARG_DEVICE
      fastboot flashall
    

If you are lucky you can then reboot your phone and the new OS will boot and you can test out critical functionality.

Note that fastboot seems to be very sensitive to the type of USB port used, the quality of the USB cable, etc. If running into trouble, try an older (USB-2 instead of USB-3 or USB-C) port, a better cable, or flashing from a different computer.

... But you're not done. You still need to sign the build, reinstall it, and lock the bootloader. The same process will generate "OTA" update images you can use to install updates. (Otherwise you have to unlock the bootloader, wiping the device, to install an update).

Generating signing keys

Generating signing keys should only be done once, then keep them in a safe and secure place. These will be your "release keys" that will replace the AOSP "test keys" from the AOSP source tree. The keys can be reused (and indeed you need to use the same keys to sign updates as were used to sign the original factory installation image). You should keep them secure as if they are compromised they could be used to compromise your phone, should your phone fall into the wrong hands (but giving them a passphrase may well cause problems). See also https://source.android.com/devices/tech/ota/sign_builds. Set the subject according to your organziational info. You need releasekey, platform, shared, media, networkstack and verified-boot keys (NOTE: networkstack is new for Android 11), e.g

      subject='/C=US/ST=California/L=SanFrancisco/O=Foobar/OU=Foobar/CN=Foobar/emailAddress=admin@example.com'
      mkdir $HOME/.android-certs
      for x in releasekey platform shared media networkstack; do \
        ./development/tools/make_key $HOME/.android-certs/$x "$subject"; \
      done
      # Also generated the verified-boot signing key
      openssl genrsa -out $HOME/.android-certs/avb.pem 2048
      external/avb/avbtool extract_public_key --key $HOME/.android-certs/avb.pem --output ~/.android-certs/avb_pkmd.bin
    

This step stored your keys in the ~/.android-certs directory. Make sure you keep them safe and secure as your device secuirity depends on them

Final build step Make sure your paths are still properly set up
	  export PATH=$HOME/platform-tools:$PATH
	  export LC_ALL=C
	  export JAVA_TOOL_OPTIONS=-Xmx6g
	  cd $HOME/android_build
	  source build/envsetup.sh
    
Update the brillo payload (not sure the exact significance of this step):
      make -j3 brillo_update_payload
    
Create the ZIP file of distribution target files:
      make -j3 dist
    
Signing the build

See also https://source.android.com/devices/tech/ota/sign_builds. Signing the build with your release keys will allow it to work securely with a locked bootloader. You will also be able to create updates that can be installed without losing the data on the phone, by installing the "OTA" updates using the recovery functions that are part of your build.

Download and edit the android_release.sh script to match your build (adjust TARG_DEVICE as appropriate and adjust my tagging -- sdh4 -- with yours). Every time you create an update I strongly recommend increment the BUILD_NUMBER (this is just used for file naming; it should not affect the anti-rollback protection bypass).

This script runs the sign_target_files_apks tool to sign the target files generated by make dist, above. It then runs the ota_from_target_files script to generate a full "OTA" (Over-the-air, but also installable from recovery) update, and img_from_target_files to create an installable "factory" image.

Run the android_release.sh script.

      bash $HOME/android_release.sh
    

(If you need to troubleshoot you can run it line by line, by pasting it into a bash shell. If you do this, leave off the various "exit 1" statements as they may cause the shell to exit).

Once android_release completes successfully you should find a directory, e.g out/release-redfin-1 containing a ZIP with signed target files, a ZIP with a "factory" image, and a ZIP with an OTA update.

Installing your new build Installing the first time
  1. Make sure you have followed the phone prep procedure (above) to enable developer mode
  2. Boot the phone into the bootloader, either by
    • Triggering a reboot from adb: adb reboot bootloader
    • With the phone off, press and hold Volume Down, then press and hold Power. You will have to hold both for perhaps 10 seconds. (Some older phones use a different key sequence)
  3. Verify that you can run the Fastboot tool, e.g.
    	  fastboot devices
    	
    should list your phone. Make sure you are using the version from the latest Android platform-tools (downloaded above)
  4. Unlocking the bootloader (THIS WILL WIPE ALL DATA FROM THE PHONE)
    	  fastboot flashing unlock
    	
  5. Installing the "factory image". THIS WILL WIPE DATA FROM THE DEVICE Unzip the "factory image" from $HOME/android_build/out/release-<DEVICE>-<BUILDNUMBER>/ and run the flash-all.sh script (no different than how you would install actual factory images)
  6. Once the flashing completes you can verify the phone still boots, but you will have to reboot again into the bootloader to install your signing key and relock the bootloader (which will once again wipe all data). Your device will show the ORANGE screen from the Android verified boot pages
  7. Installing your verified boot signing public key into the bootloader: (THIS WILL WIPE DATA FROM THE DEVICE)
    	   fastboot flash avb_custom_key $HOME/.android-certs/avb_pkmd.bin
    	
  8. Relock the bootloader THIS WILL WIPE DATA FROM THE DEVICE
    	  fastboot flashing lock
    	

    At this point you should be able to reboot and on bootup it will display the YELLOW screen shown at the bottom of the Android verified boot page. There is no way to eliminate the yellow screen short of unlocking the bootloader again and reinstalling a Google factory image.

    DO NOT DISABLE OEM UNLOCKING UNDER DEVELOPER OPTIONS (i.e. LEAVE OEM UNLOCKING ENABLED)! If you were to disable OEM unlocking and your OS were to become unbootable and you were not able to build and sign an OTA update fixing the problem, then you would not be able to reinstall a new OS or downgrade to the Google factory image.

  9. Testing security/root access:

    This is a good time to verify that the phone is secure and you have the access you need from adb. On user builds you will need to become a developer (as above) and enable USB debugging first.

    For your phone to be at all secure, if you try adb shell from your computer, the phone should prompt you to approve the connection. If it doesn't then the ro.adb.secure flag probably didn't set. If the shell prompt ends with "$" then default adb access is on a user basis. If it ends with "#" then it is on a root basis (ro.secure=0).

    If your build is supposed to be rootable you should be able to issue adb root and then adb shell to gain a root prompt (ends with "#").
  10. Now that you have basically gotten it working it is a good time to move your out/release-... directory out of the android_build tree and into a safe archiving location. I usually add the rest of the build ID to the directory name.
  11. Install apps. You will probably want to install the F-Droid open-source web store (APK Download link).

    Most importantly a web browser. F-Droid (will help you install Firefox through its FFUpdater application).

    You can install APK's such as F-Droid's that have been downloaded to your computer through adb via

    	  adb install <downloaded_file.apk>
    	

    Note that manually installed APK's will not be automatically updated.

  12. In recent Android versions, critical AOSP apps are sometimes buggy. For example I find it impossible to use the calendar in Android 10, and Messaging notifications also seem to be broken in Android 10. Fortunately there are alternative apps available in F-Droid, Etar and QKSMS replacing Calendar and Messaging respectively.
  13. You are now ready to use your new operating system! It may have a few quirks. Sometimes problems in built-in apps can be addressed by downloading a replacement from F-Droid.
  14. If you created a userdebug build, you have root-level ownership of your device. Executing adb root and then adb shell will give you a root shell!
Installing updates

Every month a new Android security bulletin rolls in, usually with a few more critical flaws, so staying updated is wise. The updates are generally posted to the AOSP download servers within 48 hours.

The procedure to build the update is largely the same as the original except you can re-use your directory tree, saving a lot of download time (do not save previous outputs or previous vendor trees within the android_build tree as the build process might get confused by the extra copy; move them out of the android_build tree if you want to save them). In the update process you will switch to the new branch (repo init), download the updated sources (repo sync), merge in your custom changes (git checkout of your custom branch and git merge in each customized repository), obtain an updated vendor tree (driver binaries or android-prepare-vendor), and recompile everything following the above process (make, make brillo_update_payload, and make dist). In addition you will need to update your BUILD_NUMBER in android_release.sh and then use it to sign the build (with your original keys). You will use the generated "OTA" zip for the update instead of the "factory" image.

Installing the update does not require unlocking the bootloader so long as it is signed with the same key you created and used previously. That means you can install the update without wiping the device. The procedure is the same as installing a Google OTA update from https://developers.google.com/android/ota. You should not lose any data (backing up anything important is wise anyway), and a lot of the potential risk is mitigated by the A/B update scheme of the Pixel and Pixel 2 series devices.

  1. Reboot into recovery: adb reboot recovery or by booting into the bootloader and using the menu to select recovery
  2. Select the "Apply update from ADB" option
  3. Use the "adb sideload" command to install the update (from $HOME/android_build/out/release-<DEVICE>-<BUILDNUMBER>/):
    	  adb sideload <ota_file.zip>
    	
  4. If you have trouble with the sideloading, the problem may be with the USB cable, or with the port or driver on your computer. It seems to be quite sensitive (see above). So try doing the update from a different port or from a different computer.
  5. If the update fails, it should boot the previous working version. The worst case scenario is that it boots the new version far enough to mark the new version as successful, but where the new version does not actually work adequately. In that case if you have bypassed rollback protection, you may well be able to reinstall the previous ota update and keep using that. If necessary you could always rebuild the previous version using the new BUILD_DATETIME, PLATFORM_SECURITY_PATCH, and PLATFORM_SECURITY_PATCH_TIMESTAMP. Rolling back versions is not guaranteed to be successful as the the failed update may have corrupted /data or modified it in non-backward-compatible ways. In the worst case, you can always unlock the bootloader (wiping all your data) and install a factory image, although you might still be limited to images with the same or newer PLATFORM_SECURITY_PATCH_TIMESTAMP once you re-lock the bootloader.
  6. Reboot once the update is complete.
Troubleshooting
  • Boot failures:

    If the boot gets to the point of the boot animation, the best troubleshooting tool is usually 'adb logcat' but this only works on debuggable (ro.debuggable=1) insecure (ro.secure=0 ro.adb.secure=0 builds), so you will probably need to switch to an eng build from user or userdebug. i.e. rerun lunch, make installclean and rebuild. In this configuration you should get immediate logging via 'adb logcat' once the Android animation screen starts on bootup. If you don't get logging check for the above flags (e.g. ro.debuggable) in android_build/out/target/product/<device>system/etc/prop.default and make sure they all have their correct values. If not a .mk file or the build property logic in build/make/core/main.mk may be setting them incorrectly. For example on walleye device/google/wahoo/device.mk sets ro.adb.secure=1 unconditionally. Another possible source of adb failing to log on bootup is incorrect selinux configuration passed through android-prepare-vendor. In general no files in selinux directories should be passed through.

    As an alternative it may be sufficient to build PRODUCT_ADB_KEYS into your build.

    In the bootup logcat look for lines with lots of stars (*******) to help find the most critical problems.
  • Buggy applications:

    You can try checking out different (perhaps older) branches/tags of problematic applications. Alternatively many applications have slight variants or extended versions in F-Droid that may work better. Sometimes adb logcat will hint at the source of the problem.
  • Troubleshooting/improving android-prepare-vendor configurations:

    • Malformed config.json:

      Use jq . config.json to check for syntax errors
    • Missing files

      A file might be gone entirely, in which case you can safely remove it from android-prepare-vendor's config.json, but more likely it has moved and/or been renamed slightly.

      With the --keep option to extract-all.sh you will find a factory_imgs_data directory within the output directory tree with the unpacked partitions from the Google factory image. You can use find to find the new name/location and then correct your device's config.json.