Skip to content

Refactor the XML diff in reproducible script#14879

Open
BarbossHack wants to merge 1 commit into
signalapp:mainfrom
BarbossHack:reproducible-refactor-axml-diff
Open

Refactor the XML diff in reproducible script#14879
BarbossHack wants to merge 1 commit into
signalapp:mainfrom
BarbossHack:reproducible-refactor-axml-diff

Conversation

@BarbossHack

@BarbossHack BarbossHack commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Contributor checklist

  • Ubuntu 24
  • Fedora 44
  • My contribution is fully baked and ready to be merged as is
  • I ensure that all the open issues my contribution fixes are mentioned in the commit message of my first commit using the Fixes #1234 syntax

Description

Context

Currently, apkdiff compares XML (and AXML) files by parsing them with Androguard and recursively comparing each element and its children, reporting differences in attributes and child counts. It then attempts to whitelist some elements that are added by the Play Store.

Problems

❌ Complex diff

Comparing additions and modifications in an XML tree and presenting them in a human-readable way is not straightforward and is error-prone.

Currently, if we remove the whitelisted elements, these are the reported differences for AndroidManifest.xml:

[
    XmlDifference(diff_type='child_count', path='manifest/application', attribute_name=None, first_value='13', second_value='16', child_tag='meta-data'), 
    XmlDifference(diff_type='attribute', path='manifest/application/meta-data', attribute_name='{http://schemas.android.com/apk/res/android}name', first_value='com.android.vending.splits', second_value='com.android.stamp.source', child_tag=None), 
    XmlDifference(diff_type='attribute', path='manifest/application/meta-data', attribute_name='{http://schemas.android.com/apk/res/android}resource', first_value='@7F180009', second_value=None, child_tag=None), 
    XmlDifference(diff_type='attribute', path='manifest/application/meta-data', attribute_name='{http://schemas.android.com/apk/res/android}value', first_value=None, second_value='https://play.google.com/store', child_tag=None), 
    XmlDifference(diff_type='attribute', path='manifest/uses-sdk', attribute_name='{http://schemas.android.com/apk/res/android}minSdkVersion', first_value='23', second_value='32', child_tag=None)
]

These differences are difficult to interpret, and some of them are misleading. For example

  • in the second line, the old value is reported as com.android.vending.splits and the new value as com.android.stamp.source. In reality, com.android.vending.splits was not modified; com.android.stamp.source was added to the XML tree.
  • The "resource" attribute is reported as having been updated to "None", but this is not the case.
  • ...

Additionally, the current whitelisting approach is too broad and ends up hiding these inconsistencies.

❌ Maintainability and reviewability

Following the findings in #14809 (comment) , I wanted to exclude minSdkVersion from the diff script. However, implementing this change in the current code made the logic harder to review, especially for people auditing the reproducibility script.

Changes introduced by this MR

  1. Refactor the XML diff function with the following flow:
    a. Parse the AXML with androguard to AXMLPrinter objects
    b. Remove the whitelisted elements from the root tree
    c. Pretty-print the XML and compare the resulting output
    d. Display any differences using a Git-style diff.

  2. Add uses-sdk//minSdkVersion to the list of whitelisted elements (as discussed in PlayStore APKs are not reproducible (8.10.2 -> 8.14.3) #14809 (comment) )

  3. Run uvx ruff format

Results

With my refactor, if I remove all whitelist entries, the output is much more readable:

Comparing: 
        /tmp/tmp.cNybtHNRBK/apks/splits/base-master.apk
        /tmp/tmp.cNybtHNRBK/org.thoughtcrime.securesms/org.thoughtcrime.securesms.apk

Unzipping...
Comparing zip entry names...
Comparing zip entry contents...
Comparing AndroidManifest.xml...
AndroidManifest.xml files differ!
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,5 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="171302" android:versionName="8.17.4" android:compileSdkVersion="36" android:compileSdkVersionCodename="16" android:requiredSplitTypes="base__abi,base__density" android:splitTypes="" package="org.thoughtcrime.securesms" platformBuildVersionCode="36" platformBuildVersionName="16">
-  <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="35"/>
+  <uses-sdk android:minSdkVersion="32" android:targetSdkVersion="35"/>
   <uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
   <uses-feature android:name="android.hardware.camera" android:required="true"/>
   <uses-feature android:name="android.hardware.location" android:required="false"/>
@@ -694,6 +694,9 @@
     </service>
     <receiver android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver" android:exported="false"/>
     <meta-data android:name="com.android.vending.splits.required" android:value="true"/>
+    <meta-data android:name="com.android.stamp.source" android:value="https://play.google.com/store"/>
+    <meta-data android:name="com.android.stamp.type" android:value="STAMP_TYPE_DISTRIBUTION_APK"/>
     <meta-data android:name="com.android.vending.splits" android:resource="@7F180009"/>
+    <meta-data android:name="com.android.vending.derived.apk.id" android:value="4"/>
   </application>
 </manifest>
APKs differ on file AndroidManifest.xml! Files extracted to the mismatches/ directory.
Comparing resources.arsc...
APKs don't match!

I've tested this MR by comparing the following APKs:

  • Signal-Android v8.17.4 split from Play Store, website, github releases: 28967945413
  • Olvid split from Play Store and fdroid: 29124109912
  • Telegram split from Play Store and website: 28968095127
  • Threema from website: 28970249962
  • Briar split from Play Store and website: 28970782075

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant