Skip to content

tilaksidduram/android_packages_apps_OpenDelta

Repository files navigation

About

OpenDelta was written to provide automatic OTA updates for The OmniROM Project's nightly builds, making use of deltas when possible, to reduce the size of the download.

There's no reason you couldn't use it for weeklies or monthlies or milestones as well though!

License

OpenDelta is licensed under the terms of the GNU General Public License, version 3.0. See the COPYING file for the full license text.

How

OpenDelta uses binary differentials (VCDIFF, RFC 3284) between the previous and the current release, courtesy of xdelta3 (http://xdelta.org/).

Usually, OTA ZIP files created by Android builds are compressed. Diffs between compressed OTA ZIPs are not ideal - they are significantly larger than the diff between two uncompressed OTA ZIPs would be. So before we create the diff we decompress the contents of the OTA ZIPs.

The whole-file signature of the OTA ZIP is broken (and removed) by this process, and so we also re-sign the decompressed ZIPs with the same keys used to build Android. We create a second diff between the unsigned and re-signed ZIP file, so if needed the client can re-create a properly signed ZIP file.

The produced delta files are pushed to the public download server, and the current build is saved to a private location to serve as input for the next differential run.

It is important to note that the differential files are named after the input file, not after the output file. Initially this may seem a bit confusing when working with these files, but this way the client doesn't need to know any information about future builds when looking for updates, and no server logic is needed at all - just a public download location - as the delta filename can be reconstructed from getprop's on the device.

The Android client periodically checks in with the download server and retrieves the .delta file for its current build. After parsing it, it knows the name for the next build as well, and then the one after that, etc. So if you don't update for a number of builds, it can still reconstruct the latest build by chaining the deltas. It will check each delta if we already have intermediate files present - perhaps we already performed the work for the last build but never flashed it, for example. Based on all this information it will decide to either reconstruct the final flashable ZIP, or just download the latest full OTA and flash that.

Compatibility

OpenDelta is developed for use with TWRP, and uses scripting to accomplish its tasks. Other recoveries with full OpenRecoveryScript may work as well, but are not tested against.

CWM is not officially supported by OpenDelta, though if not operating in secure mode, a script that may work with community-built CWM versions is generated as well. Official CWM builds (acquired from the CWM website or installed by ROM Manager) are not supported as they disable scripting capabilities. Even if this script works with your build, you may encounter it using the wrong storage paths, failing verification, producing various errors, etc.

Security

The OTA ZIPs that OpenDelta downloads or re-generates are stored on either internal or external storage. These locations are not secure, as any malicious app can write to these locations, and with some careful timing place its own update to be flashed instead of our update, thus gaining full system access.

Additionally, OpenDelta conveniently flashes ZIPs located in the FlashAfterUpdate subfolder of its storage. A malicious app could add its own ZIPs to the list, thus gaining full system access.

OpenDelta has the capability to re-generate OTA ZIPs fully signed with your private keys (without knowing them). Assuming you aren't using a set of publicly known keys to sign your ZIPs (ouch!), this can be used to make your update secure.

Chances are that the recovery you are using does not have your public key built-in for whole-file verification purposes, and thus verification would fail. This is why OpenDelta also provides the capability to inject your public key into the recovery. This public key is provided to the recovery through the /cache partition, which non-privileged apps cannot write to.

These features combined allows the recovery to verify the update signature securely without the chance of a malicious app hijacking either the update or the keys. However, this feature only works with TWRP, and the signatures will not be checked by non-OpenRecoveryScript recoveries. It also leaves open the FlashAfterUpdate hole, as ZIPs stored there by the user will (likely) not be signed with the same keys as your update, and thus their origins cannot be verified.

If OpenDelta is configured with all the needed parts to re-generate the OTA ZIPs fully signed, and verify the signatures in recovery, then secure mode becomes available (whether or not it is enabled by default is also a configuration switch). In secure mode, the public key injection and signature verification features are enabled, additional ZIPs from the FlashAfterUpdate subfolder will not be flashed, and the CWM-compatibile script will not be generated. Unless your recovery is compromised, this should provide for fully secure flashing.

Of course, the user has the option to enable or disable this feature from the actionbar menu.

Bad builds

As OpenDelta depends on an unbroken chain of deltas, you can't just remove the files of a bad/dangerous/etc build. If you want to prevent the client from producing and flashing such a build, rename the relevant .delta file to .delta_revoked.

We'd still have a problem if you want to produce a replacement build, or for some reason have several different builds with the same name, and this is breaking the chain of deltas. The solution for this is to edit the relevant .delta file, and setting the size of the update file to a value larger than the size_official of the out file. This will trigger the client to download the full-size compressed OTA ZIP instead.

Server-side

To create the delta files on the server, you need several things, some of which can be found in the server directory. The main thing is the opendelta.sh script. It contains a configuration section which you can edit with the correct file locations on your own system. Quite likely you will need to create a wrapper script that pulls in your previous release and your current release, and pushes out the created delta files.

The script depends on xdelta3, zipadjust and minsignapk.

For the builds on your server, make a copy of the jni directory - do not compile inside jni because you may mess up the build of libopendelta.

xdelta3 can be built in (the copy of) jni/xdelta3-3.0.7 by calling ./configure and make.

zipadjust can be built in (the copy of) jni by calling:

gcc -o zipadjust zipadjust.c zipadjust_run.c -lz

dedelta (not used by the script, but maybe helpful when debugging) can be built in (the copy of) jni by calling:

gcc -o dedelta xdelta3-3.0.7/xdelta3.c delta.c delta_run.c

minsignapk Java source is in the server directory, as well as a prebuilt minsignapk.jar file that should work on most systems

Eclipse

For debugging purposes you may wish to build in Eclipse instead of an Android tree, for test-speed benefits. The native part of OpenDelta is also NDK buildable.

You may need to enable the app to show up in the launcher ("System Updates") by editing AndroidManifest.

The APK needs privileged system permissions, and thus needs to placed in /system/priv-app. If you're testing on a build that includes OpenDelta already, remove it from that location and reboot before continuing. If you install the APK through Eclipse it'll end up in /data/app, but will not be granted the right permissions. Move that APK to /system/priv-app and reboot. Now increase the versionCode in AndroidManifest to a larger number, and your Eclipse-installed builds will magically run with the right permissions granted every time. You could use pm grant but you'd have to do that after every install.

Aside from inside the APK, you also need to place the produced libopendelta.so for your architecture in /system/lib. If you're actually working on the native library this gets annoying fast, symlinking that location to the library location from the APK can save you a lot of headache.

-EOF-