Signal-Android icon indicating copy to clipboard operation
Signal-Android copied to clipboard

Reproducible Builds are broken

Open obfusk opened this issue 1 year ago • 16 comments

  • [x] I have searched open and closed issues for duplicates
  • [x] I am submitting a bug report for existing functionality that does not work as intended
  • [x] I have read https://github.com/signalapp/Signal-Android/wiki/Submitting-useful-bug-reports
  • [x] This isn't a feature request or a discussion topic

Bug description

Building an APK using the provided instructions and docker container results in APKs with differences in classes*.dex (and baseline.profm) compared to the official APKs published on Google Play (play flavour) and updates.signal.org (website flavour).

NB: I have found zero evidence of any kind of compromise. Some differences are yet unexplained but everything I found seems to be benign. I am disappointed that Reproducible Builds have been broken for months but I have zero reason to doubt Signal's security in any way.

Steps to reproduce

  • build an APK following the instructions in reproducible-builds/README.md
    • https://github.com/obfusk/Signal-Android/actions/runs/9133817464 (assemblePlayProdRelease, v7.6.2, arm64-v8a)
    • https://github.com/obfusk/Signal-Android/actions/runs/9133816918 (assemblePlayProdRelease, v7.7.1, arm64-v8a)
    • https://github.com/obfusk/Signal-Android/actions/runs/9133818588 (assembleWebsiteProdRelease, v7.6.2, universal)
  • compare the APK to the official APK

Actual result:

APKs with differences in classes*.dex (and baseline.profm).

(Also baseline.prof but that's a result of it containing a checksum for the differing .dex files.)

Expected result:

Identical APKs (except for the APK/JAR signature and possibly the baseline.profm).

Details

classes*.dex: non-deterministic order in Project.languageList() in app/build.gradle.kts

Introduced by ac5d0bf8a3119ad21732bf422706652f93ee095c on Dec 4, 2023 and apparently unnoticed since.

The old app/build.gradle code used .sort():

def autoResConfig() {
    def files = []
    allStringsResourceFiles { f ->
        files.add(f.parentFile.name)
    }
    ['en'] + files.collect { f -> f =~ /^values-([a-z]{2,3}(-r[A-Z]{2})?)$/ }
            .findAll { matcher -> matcher.find() }
            .collect { matcher -> matcher.group(1) }
            .sort()
}

The new app/build.gradle.kts code is missing .sorted(), resulting in a non-deterministic order as "The order of the files in a FileTree is not stable, even on a single computer.".

fun Project.languageList(): List<String> {
  return fileTree("src/main/res") { include("**/strings.xml") }
    .map { stringFile -> stringFile.parentFile.name }
    .map { valuesFolderName -> valuesFolderName.replace("values-", "") }
    .filter { valuesFolderName -> valuesFolderName != "values" }
    .map { languageCode -> languageCode.replace("-r", "_") }
    .distinct() + "en"
}

Should be e.g.:

fun Project.languageList(): List<String> {
  return fileTree("src/main/res") { include("**/strings.xml") }
    .map { stringFile -> stringFile.parentFile.name }
    .map { valuesFolderName -> valuesFolderName.replace("values-", "") }
    .filter { valuesFolderName -> valuesFolderName != "values" }
    .map { languageCode -> languageCode.replace("-r", "_") }
    .distinct().sorted() + "en"
}

Relevant part of dexdump diff:

 | org.thoughtcrime.securesms.BuildConfig.<clinit>:()V
-|: const-string/jumbo v0, "uk"
-|: const-string/jumbo v1, "mk"
-|: const-string v2, "ar"
-|: const-string v3, "ko"
-|: const-string v4, "da"
-|: const-string v5, "bn"
-|: const-string/jumbo v6, "nb"
-|: const-string/jumbo v7, "lt"
-|: const-string v8, "bs"
-|: const-string v9, "gu"
-|: const-string/jumbo v10, "yue"
-|: const-string/jumbo v11, "tr"
-|: const-string/jumbo v12, "te"
-|: const-string v13, "fi"
-|: const-string/jumbo v14, "ro"
-|: const-string v15, "hi"
-|: const-string v16, "ja"
-|: const-string v17, "bg"
-|: const-string v18, "kn"
-|: const-string v19, "km"
-|: const-string v20, "af"
-|: const-string v21, "et"
-|: const-string v22, "in"
-|: const-string/jumbo v23, "sq"
-|: const-string/jumbo v24, "zh_HK"
-|: const-string v25, "fr"
-|: const-string v26, "kk"
-|: const-string/jumbo v27, "ms"
-|: const-string v28, "cs"
-|: const-string v29, "ca"
-|: const-string v30, "hu"
-|: const-string/jumbo v31, "ml"
-|: const-string v32, "el"
-|: const-string/jumbo v33, "ta"
-|: const-string/jumbo v34, "pa"
-|: const-string/jumbo v35, "th"
-|: const-string v36, "az"
-|: const-string/jumbo v37, "ug"
-|: const-string/jumbo v38, "nl"
-|: const-string/jumbo v39, "sw"
-|: const-string v40, "it"
-|: const-string v41, "de"
-|: const-string/jumbo v42, "vi"
+|: const-string v0, "es"
+|: const-string v1, "gu"
+|: const-string v2, "bn"
+|: const-string/jumbo v3, "sv"
+|: const-string v4, "ca"
+|: const-string/jumbo v5, "pt_BR"
+|: const-string/jumbo v6, "pt"
+|: const-string v7, "bs"
+|: const-string/jumbo v8, "zh_HK"
+|: const-string v9, "fa"
+|: const-string v10, "da"
+|: const-string/jumbo v11, "ml"
+|: const-string/jumbo v12, "sq"
+|: const-string/jumbo v13, "tr"
+|: const-string v14, "ja"
+|: const-string/jumbo v15, "zh_CN"
+|: const-string v16, "az"
+|: const-string/jumbo v17, "nl"
+|: const-string v18, "el"
+|: const-string v19, "et"
+|: const-string/jumbo v20, "pa"
+|: const-string/jumbo v21, "sk"
+|: const-string v22, "ar"
+|: const-string/jumbo v23, "sr"
+|: const-string v24, "kk"
+|: const-string/jumbo v25, "my"
+|: const-string/jumbo v26, "nb"
+|: const-string/jumbo v27, "vi"
+|: const-string/jumbo v28, "pl"
+|: const-string/jumbo v29, "ro"
+|: const-string/jumbo v30, "lt"
+|: const-string/jumbo v31, "ru"
+|: const-string v32, "ky"
+|: const-string/jumbo v33, "uk"
+|: const-string v34, "it"
+|: const-string v35, "ko"
+|: const-string v36, "cs"
+|: const-string v37, "fr"
+|: const-string/jumbo v38, "mk"
+|: const-string/jumbo v39, "ug"
+|: const-string/jumbo v40, "lv"
+|: const-string v41, "iw"
+|: const-string v42, "hi"
 |: const-string v43, "ga"
-|: const-string/jumbo v44, "zh_CN"
+|: const-string v44, "fi"
 |: const-string v45, "hr"
-|: const-string v46, "gl"
-|: const-string/jumbo v47, "tl"
-|: const-string/jumbo v48, "lv"
-|: const-string/jumbo v49, "my"
-|: const-string/jumbo v50, "ru"
-|: const-string v51, "ky"
-|: const-string v52, "es"
-|: const-string/jumbo v53, "sk"
-|: const-string/jumbo v54, "sv"
-|: const-string v55, "fa"
-|: const-string/jumbo v56, "sr"
-|: const-string v57, "ka"
-|: const-string v58, "eu"
-|: const-string/jumbo v59, "sl"
-|: const-string/jumbo v60, "ur"
-|: const-string/jumbo v61, "pl"
-|: const-string/jumbo v62, "pt"
-|: const-string/jumbo v63, "mr"
-|: const-string v64, "iw"
-|: const-string/jumbo v65, "zh_TW"
-|: const-string/jumbo v66, "pt_BR"
+|: const-string v46, "ka"
+|: const-string/jumbo v47, "yue"
+|: const-string v48, "hu"
+|: const-string v49, "af"
+|: const-string/jumbo v50, "th"
+|: const-string v51, "km"
+|: const-string/jumbo v52, "tl"
+|: const-string/jumbo v53, "zh_TW"
+|: const-string v54, "de"
+|: const-string v55, "eu"
+|: const-string/jumbo v56, "sl"
+|: const-string v57, "kn"
+|: const-string v58, "bg"
+|: const-string/jumbo v59, "ms"
+|: const-string/jumbo v60, "ta"
+|: const-string/jumbo v61, "te"
+|: const-string/jumbo v62, "mr"
+|: const-string v63, "gl"
+|: const-string/jumbo v64, "sw"
+|: const-string/jumbo v65, "ur"
+|: const-string v66, "in"
 |: const-string v67, "en"
 |: filled-new-array/range {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63, v64, v65, v66, v67}, [Ljava/lang/String;

classes*.dex: unexplained differences

These differences occur in several classes*.dex files; they are identical for both play and website v7.6.2 ("variant 2") but different for play v7.7.1 ("variant 1").

Variant 1 (play v7.7.1):

   VISIBILITY_SYSTEM Ldalvik/annotation/MemberClasses; value={ Lorg/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections$ActionRestoreFromBacakupFragmentToMoreOptions; }
@@ -570943,6 +570943,23 @@
         0x0000 - 0x0004 reg=0 this Lorg/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections; 
 
     #1              : (in Lorg/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections;)
+      name          : 'actionRestartToWelcomeFragment'
+      type          : '()Landroidx/navigation/NavDirections;'
+      access        : 0x0009 (PUBLIC STATIC)
+      code          -
+      registers     : 1
+      ins           : 0
+      outs          : 0
+      insns size    : 5 16-bit code units
+| org.thoughtcrime.securesms.backup.v2.ui.restore.RestoreFromBackupFragmentDirections.actionRestartToWelcomeFragment:()Landroidx/navigation/NavDirections;
+|: invoke-static {}, Lorg/thoughtcrime/securesms/SignupDirections;.actionRestartToWelcomeFragment:()Landroidx/navigation/NavDirections;
+|: move-result-object v0
+|: return-object v0
+      catches       : (none)
+      positions     : 
+      locals        : 
+
+    #2              : (in Lorg/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections;)
       name          : 'actionRestoreFromBacakupFragmentToMoreOptions'
       type          : '(Lorg/thoughtcrime/securesms/devicetransfer/moreoptions/MoreTransferOrRestoreOptionsMode;)Lorg/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections$ActionRestoreFromBacakupFragmentToMoreOptions;'
       access        : 0x0009 (PUBLIC STATIC)

Variant 2 (play and website v7.6.2):

     #1              : (in Lorg/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections;)
-      name          : 'actionRestartToWelcomeFragment'
+      name          : 'actionRestartToWelcomeV2Fragment'
       type          : '()Landroidx/navigation/NavDirections;'
       access        : 0x0009 (PUBLIC STATIC)
       code          -
@@ -569000,8 +569000,8 @@
       ins           : 0
       outs          : 0
       insns size    : 5 16-bit code units
-| org.thoughtcrime.securesms.backup.v2.ui.restore.RestoreFromBackupFragmentDirections.actionRestartToWelcomeFragment:()Landroidx/navigation/NavDirections;
-|: invoke-static {}, Lorg/thoughtcrime/securesms/SignupDirections;.actionRestartToWelcomeFragment:()Landroidx/navigation/NavDirections;
+| org.thoughtcrime.securesms.backup.v2.ui.restore.RestoreFromBackupFragmentDirections.actionRestartToWelcomeV2Fragment:()Landroidx/navigation/NavDirections;
+|: invoke-static {}, Lorg/thoughtcrime/securesms/SignupV2Directions;.actionRestartToWelcomeV2Fragment:()Landroidx/navigation/NavDirections;
 |: move-result-object v0
 |: return-object v0
       catches       : (none)

baseline.profm

The assets/dexopt/baseline.profm file is generated non-deterministically; both a workaround and a proper fix have been available for over a year, but instead of using either apkdiff.py simply skips this file.

Upstream bug (fixed in AGP 8.1.0-alpha03):

  • https://issuetracker.google.com/issues/231837768

Analysis:

  • https://gist.github.com/obfusk/61046e09cee352ae6dd109911534b12e

Workaround:

  • https://gist.github.com/obfusk/eb82a810ed6aad266dab19977b18cee6

Workaround used by Threema (until no longer needed because AGP was updated to a version with the fix):

  • https://github.com/threema-ch/threema-android/commit/666ac380f1b5e61b282d283da612680719d64a43#diff-51a0b488f963eb0be6c6599bf5df497313877cf5bdff3950807373912ac1cdc9R605

Workaround used by Tor Browser:

  • https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/merge_requests/781

apkdiff.py

Even without the reproducibility issues outlined above, use of apkdiff.py instead of producing a bit-by-bit identical APK file does not meet the definition of "Reproducible Build" as used by reproducible-builds.org.

See https://reproducible-builds.org/reports/2022-12/#android-news.

Screenshots

n/a

Device info

n/a

Link to debug log

n/a

obfusk avatar May 17 '24 23:05 obfusk

Decompiled Java code diffs (a bit easier to read than dexdump output):

Variant 1:

--- x/classes4-enjarify/org/thoughtcrime/securesms/BuildConfig.class.java	2024-05-19 00:28:05.439481781 +0200
+++ y/classes4-enjarify/org/thoughtcrime/securesms/BuildConfig.class.java	2024-05-19 00:28:06.135487756 +0200
@@ -67,7 +67,7 @@
     public static final String ZKGROUP_SERVER_PUBLIC_PARAMS = "AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P+NameAZYOD12qRkxosQQP5uux6B2nRyZ7sAV54DgFyLiRcq1FvwKw2EPQdk4HDoePrO/RNUbyNddnM/mMgj4FW65xCoT1LmjrIjsv/Ggdlx46ueczhMgtBunx1/w8k8V+l8LVZ8gAT6wkU5J+DPQalQguMg12Jzug3q4TbdHiGCmD9EunCwOmsLuLJkz6EcSYXtrlDEnAM+hicw7iergYLLlMXpfTdGxJCWJmP4zqUFeTTmsmhsjGBt7NiEB/9pFFEB3pSbf4iiUukw63Eo8Aqnf4iwob6X1QviCWuc8t0LUlT9vALgh/f2DPVOOmR0RW6bgRvc7DSF20V/omg+YBw==";
     
     static {
-        LANGUAGES = new String[] { "uk", "mk", "ar", "ko", "da", "bn", "nb", "lt", "bs", "gu", "yue", "tr", "te", "fi", "ro", "hi", "ja", "bg", "kn", "km", "af", "et", "in", "sq", "zh_HK", "fr", "kk", "ms", "cs", "ca", "hu", "ml", "el", "ta", "pa", "th", "az", "ug", "nl", "sw", "it", "de", "vi", "ga", "zh_CN", "hr", "gl", "tl", "lv", "my", "ru", "ky", "es", "sk", "sv", "fa", "sr", "ka", "eu", "sl", "ur", "pl", "pt", "mr", "iw", "zh_TW", "pt_BR", "en" };
+        LANGUAGES = new String[] { "es", "gu", "bn", "sv", "ca", "pt_BR", "pt", "bs", "zh_HK", "fa", "da", "ml", "sq", "tr", "ja", "zh_CN", "az", "nl", "el", "et", "pa", "sk", "ar", "sr", "kk", "my", "nb", "vi", "pl", "ro", "lt", "ru", "ky", "uk", "it", "ko", "cs", "fr", "mk", "ug", "lv", "iw", "hi", "ga", "fi", "hr", "ka", "yue", "hu", "af", "th", "km", "tl", "zh_TW", "de", "eu", "sl", "kn", "bg", "ms", "ta", "te", "mr", "gl", "sw", "ur", "in", "en" };
         LIBSIGNAL_NET_ENV = Network$Environment.PRODUCTION;
         final String s = "104.18.37.148";
         final String s2 = "172.64.150.108";
--- x/classes4-enjarify/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections.class.java	2024-05-19 00:28:06.651492185 +0200
+++ y/classes4-enjarify/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections.class.java	2024-05-19 00:28:07.243497268 +0200
@@ -5,12 +5,18 @@
 package org.thoughtcrime.securesms.backup.v2.ui.restore;
 
 import org.thoughtcrime.securesms.devicetransfer.moreoptions.MoreTransferOrRestoreOptionsMode;
+import org.thoughtcrime.securesms.SignupDirections;
+import androidx.navigation.NavDirections;
 
 public class RestoreFromBackupFragmentDirections
 {
     private RestoreFromBackupFragmentDirections() {
     }
     
+    public static NavDirections actionRestartToWelcomeFragment() {
+        return SignupDirections.actionRestartToWelcomeFragment();
+    }
+    
     public static RestoreFromBackupFragmentDirections$ActionRestoreFromBacakupFragmentToMoreOptions actionRestoreFromBacakupFragmentToMoreOptions(final MoreTransferOrRestoreOptionsMode moreTransferOrRestoreOptionsMode) {
         return new RestoreFromBackupFragmentDirections$ActionRestoreFromBacakupFragmentToMoreOptions(moreTransferOrRestoreOptionsMode, null);
     }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferCompleteFragmentDirections.class.java	2024-05-19 00:28:07.871502659 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferCompleteFragmentDirections.class.java	2024-05-19 00:28:08.539508393 +0200
@@ -4,6 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
+import org.thoughtcrime.securesms.SignupDirections;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -15,4 +16,8 @@
     public static NavDirections actionNewDeviceTransferCompleteToEnterPhoneNumberFragment() {
         return (NavDirections)new ActionOnlyNavDirections(2131362048);
     }
+    
+    public static NavDirections actionRestartToWelcomeFragment() {
+        return SignupDirections.actionRestartToWelcomeFragment();
+    }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferFragmentDirections.class.java	2024-05-19 00:28:09.063512892 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferFragmentDirections.class.java	2024-05-19 00:28:09.623517699 +0200
@@ -4,6 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
+import org.thoughtcrime.securesms.SignupDirections;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -19,4 +20,8 @@
     public static NavDirections actionNewDeviceTransferToNewDeviceTransferInstructions() {
         return (NavDirections)new ActionOnlyNavDirections(2131362050);
     }
+    
+    public static NavDirections actionRestartToWelcomeFragment() {
+        return SignupDirections.actionRestartToWelcomeFragment();
+    }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferInstructionsFragmentDirections.class.java	2024-05-19 00:28:10.243523022 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferInstructionsFragmentDirections.class.java	2024-05-19 00:28:10.835528104 +0200
@@ -4,6 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
+import org.thoughtcrime.securesms.SignupDirections;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -15,4 +16,8 @@
     public static NavDirections actionDeviceTransferSetup() {
         return (NavDirections)new ActionOnlyNavDirections(2131361952);
     }
+    
+    public static NavDirections actionRestartToWelcomeFragment() {
+        return SignupDirections.actionRestartToWelcomeFragment();
+    }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferSetupFragmentDirections.class.java	2024-05-19 00:28:11.347532499 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferSetupFragmentDirections.class.java	2024-05-19 00:28:11.915537376 +0200
@@ -4,6 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
+import org.thoughtcrime.securesms.SignupDirections;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -19,4 +20,8 @@
     public static NavDirections actionNewDeviceTransfer() {
         return (NavDirections)new ActionOnlyNavDirections(2131362051);
     }
+    
+    public static NavDirections actionRestartToWelcomeFragment() {
+        return SignupDirections.actionRestartToWelcomeFragment();
+    }
 }

Variant 2:

--- x/classes4-enjarify/org/thoughtcrime/securesms/BuildConfig.class.java	2024-05-19 00:16:05.569301994 +0200
+++ y/classes4-enjarify/org/thoughtcrime/securesms/BuildConfig.class.java	2024-05-19 00:16:06.333308553 +0200
@@ -67,7 +67,7 @@
     public static final String ZKGROUP_SERVER_PUBLIC_PARAMS = "AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P+NameAZYOD12qRkxosQQP5uux6B2nRyZ7sAV54DgFyLiRcq1FvwKw2EPQdk4HDoePrO/RNUbyNddnM/mMgj4FW65xCoT1LmjrIjsv/Ggdlx46ueczhMgtBunx1/w8k8V+l8LVZ8gAT6wkU5J+DPQalQguMg12Jzug3q4TbdHiGCmD9EunCwOmsLuLJkz6EcSYXtrlDEnAM+hicw7iergYLLlMXpfTdGxJCWJmP4zqUFeTTmsmhsjGBt7NiEB/9pFFEB3pSbf4iiUukw63Eo8Aqnf4iwob6X1QviCWuc8t0LUlT9vALgh/f2DPVOOmR0RW6bgRvc7DSF20V/omg+YBw==";
     
     static {
-        LANGUAGES = new String[] { "my", "cs", "ur", "eu", "sl", "zh_TW", "es", "ga", "uk", "zh_HK", "bn", "gu", "ko", "it", "km", "fi", "ta", "tr", "gl", "hi", "pa", "iw", "tl", "lv", "pt_BR", "az", "sq", "ms", "ug", "pt", "ka", "nb", "fr", "mr", "kk", "hu", "el", "af", "lt", "de", "ru", "et", "bs", "mk", "ar", "ca", "ro", "sw", "sk", "zh_CN", "sr", "yue", "fa", "in", "th", "te", "kn", "ml", "vi", "nl", "sv", "ky", "hr", "pl", "bg", "da", "ja", "en" };
+        LANGUAGES = new String[] { "es", "gu", "bn", "sv", "ca", "pt_BR", "pt", "bs", "zh_HK", "fa", "da", "ml", "sq", "tr", "ja", "zh_CN", "az", "nl", "el", "et", "pa", "sk", "ar", "sr", "kk", "my", "nb", "vi", "pl", "ro", "lt", "ru", "ky", "uk", "it", "ko", "cs", "fr", "mk", "ug", "lv", "iw", "hi", "ga", "fi", "hr", "ka", "yue", "hu", "af", "th", "km", "tl", "zh_TW", "de", "eu", "sl", "kn", "bg", "ms", "ta", "te", "mr", "gl", "sw", "ur", "in", "en" };
         LIBSIGNAL_NET_ENV = Network$Environment.PRODUCTION;
         final String s = "104.18.37.148";
         final String s2 = "172.64.150.108";
--- x/classes4-enjarify/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections.class.java	2024-05-19 00:16:06.833312845 +0200
+++ y/classes4-enjarify/org/thoughtcrime/securesms/backup/v2/ui/restore/RestoreFromBackupFragmentDirections.class.java	2024-05-19 00:16:07.349317275 +0200
@@ -5,7 +5,7 @@
 package org.thoughtcrime.securesms.backup.v2.ui.restore;
 
 import org.thoughtcrime.securesms.devicetransfer.moreoptions.MoreTransferOrRestoreOptionsMode;
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class RestoreFromBackupFragmentDirections
@@ -13,8 +13,8 @@
     private RestoreFromBackupFragmentDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
     
     public static RestoreFromBackupFragmentDirections$ActionRestoreFromBacakupFragmentToMoreOptions actionRestoreFromBacakupFragmentToMoreOptions(final MoreTransferOrRestoreOptionsMode moreTransferOrRestoreOptionsMode) {
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/moreoptions/MoreTransferOrRestoreOptionsSheetDirections.class.java	2024-05-19 00:16:07.813321258 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/moreoptions/MoreTransferOrRestoreOptionsSheetDirections.class.java	2024-05-19 00:16:08.305325482 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.moreoptions;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class MoreTransferOrRestoreOptionsSheetDirections
@@ -12,7 +12,7 @@
     private MoreTransferOrRestoreOptionsSheetDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferCompleteFragmentDirections.class.java	2024-05-19 00:16:08.861330255 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferCompleteFragmentDirections.class.java	2024-05-19 00:16:09.389334788 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -13,11 +13,11 @@
     private NewDeviceTransferCompleteFragmentDirections() {
     }
     
-    public static NavDirections actionNewDeviceTransferCompleteToEnterPhoneNumberFragment() {
-        return (NavDirections)new ActionOnlyNavDirections(2131362048);
+    public static NavDirections actionNewDeviceTransferCompleteToEnterPhoneNumberV2Fragment() {
+        return (NavDirections)new ActionOnlyNavDirections(2131362049);
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferFragmentDirections.class.java	2024-05-19 00:16:09.921339355 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferFragmentDirections.class.java	2024-05-19 00:16:10.505344368 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -21,7 +21,7 @@
         return (NavDirections)new ActionOnlyNavDirections(2131362051);
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferInstructionsFragmentDirections.class.java	2024-05-19 00:16:11.057349107 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferInstructionsFragmentDirections.class.java	2024-05-19 00:16:11.601353777 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -17,7 +17,7 @@
         return (NavDirections)new ActionOnlyNavDirections(2131361952);
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferSetupFragmentDirections.class.java	2024-05-19 00:16:12.189358825 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceTransferSetupFragmentDirections.class.java	2024-05-19 00:16:12.725363426 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -21,7 +21,7 @@
         return (NavDirections)new ActionOnlyNavDirections(2131362052);
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/TransferOrRestoreFragmentDirections.class.java	2024-05-19 00:16:13.257367993 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/devicetransfer/newdevice/TransferOrRestoreFragmentDirections.class.java	2024-05-19 00:16:13.769372389 +0200
@@ -5,7 +5,7 @@
 package org.thoughtcrime.securesms.devicetransfer.newdevice;
 
 import org.thoughtcrime.securesms.devicetransfer.moreoptions.MoreTransferOrRestoreOptionsMode;
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -22,8 +22,8 @@
         return (NavDirections)new ActionOnlyNavDirections(2131362053);
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
     
     public static TransferOrRestoreFragmentDirections$ActionTransferOrRestoreToMoreOptions actionTransferOrRestoreToMoreOptions(final MoreTransferOrRestoreOptionsMode moreTransferOrRestoreOptionsMode) {
--- x/classes5-enjarify/org/thoughtcrime/securesms/preferences/EditProxyFragmentDirections.class.java	2024-05-19 00:16:14.309377024 +0200
+++ y/classes5-enjarify/org/thoughtcrime/securesms/preferences/EditProxyFragmentDirections.class.java	2024-05-19 00:16:14.829381488 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.preferences;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class EditProxyFragmentDirections
@@ -12,7 +12,7 @@
     private EditProxyFragmentDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/AccountLockedFragmentDirections.class.java	2024-05-19 00:16:15.397386364 +0200
+++ y/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/AccountLockedFragmentDirections.class.java	2024-05-19 00:16:15.981391378 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.registration.fragments;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class AccountLockedFragmentDirections
@@ -12,7 +12,7 @@
     private AccountLockedFragmentDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/CaptchaFragmentDirections.class.java	2024-05-19 00:16:16.581396529 +0200
+++ y/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/CaptchaFragmentDirections.class.java	2024-05-19 00:16:17.229402091 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.registration.fragments;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class CaptchaFragmentDirections
@@ -12,7 +12,7 @@
     private CaptchaFragmentDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/ChooseBackupFragmentDirections.class.java	2024-05-19 00:16:17.857407483 +0200
+++ y/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/ChooseBackupFragmentDirections.class.java	2024-05-19 00:16:18.545413389 +0200
@@ -5,7 +5,7 @@
 package org.thoughtcrime.securesms.registration.fragments;
 
 import androidx.navigation.ActionOnlyNavDirections;
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class ChooseBackupFragmentDirections
@@ -13,8 +13,8 @@
     private ChooseBackupFragmentDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
     
     public static ChooseBackupFragmentDirections$ActionRestore actionRestore() {
--- x/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/CountryPickerFragmentDirections.class.java	2024-05-19 00:16:19.137418470 +0200
+++ y/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/CountryPickerFragmentDirections.class.java	2024-05-19 00:16:19.753423759 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.registration.fragments;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class CountryPickerFragmentDirections
@@ -12,7 +12,7 @@
     private CountryPickerFragmentDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/RegistrationCompleteFragmentDirections.class.java	2024-05-19 00:16:20.373429082 +0200
+++ y/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/RegistrationCompleteFragmentDirections.class.java	2024-05-19 00:16:20.929433855 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.registration.fragments;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.NavDirections;
 
 public class RegistrationCompleteFragmentDirections
@@ -12,7 +12,7 @@
     private RegistrationCompleteFragmentDirections() {
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
 }
--- x/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragmentDirections.class.java	2024-05-19 00:16:21.565439314 +0200
+++ y/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragmentDirections.class.java	2024-05-19 00:16:22.201444774 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.registration.fragments;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -17,8 +17,8 @@
         return (NavDirections)new ActionOnlyNavDirections(2131361856);
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
     
     public static NavDirections actionSuccessfulRegistration() {
--- x/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragmentDirections.class.java	2024-05-19 00:16:22.773449685 +0200
+++ y/classes6-enjarify/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragmentDirections.class.java	2024-05-19 00:16:23.393455007 +0200
@@ -4,7 +4,7 @@
 
 package org.thoughtcrime.securesms.registration.fragments;
 
-import org.thoughtcrime.securesms.SignupDirections;
+import org.thoughtcrime.securesms.SignupV2Directions;
 import androidx.navigation.ActionOnlyNavDirections;
 import androidx.navigation.NavDirections;
 
@@ -21,8 +21,8 @@
         return (NavDirections)new ActionOnlyNavDirections(2131362054);
     }
     
-    public static NavDirections actionRestartToWelcomeFragment() {
-        return SignupDirections.actionRestartToWelcomeFragment();
+    public static NavDirections actionRestartToWelcomeV2Fragment() {
+        return SignupV2Directions.actionRestartToWelcomeV2Fragment();
     }
     
     public static NavDirections actionSkip() {

obfusk avatar May 18 '24 22:05 obfusk

Hello,

Thanks for the detailed analysis!

  1. The language list should be easy enough to include sort; good catch.
  2. These are methods are generated from XML using the AndroidX Navigation component. Specifically, this navigation graph is under active development and I am relying on some undefined behavior (re-using a resource ID for a navigation destination) to keep both the old & new graphs both working. Hopefully this sufficiently explains the differences and similarities of actionRestartToWelcomeFragment and actionRestartToWelcomeV2Fragment
  3. Upgrading our toolchain, including AGP, is something we are focusing on for v7.9

nicholas-signal avatar May 20 '24 16:05 nicholas-signal

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 20 '24 06:07 stale[bot]

Huh, I'm surprised this wasn't caught on our end either! We have a nightly job that builds three APK's from scratch, signs one, and does cross comparisons between all of them as a means to wean out and non-determinism... I wonder if the system just tended to always give the files in the same order.

Thank you very much for telling us about the AGP 8.1 fixes! I will try to remove some of the files we've been ignoring and check to make sure things still verify. Since Nicholas posted we have indeed fixed the language bug and upgraded all the way to AGP 8.4, so hopefully that's all resolved. Not sure about Navigation bug, but it's not causing issues in the setup I described above, so curious if you're still hitting it.

Also, I was reading up on your work and found https://github.com/obfusk/apksigcopier -- a very cool project! When I get some time I'll see if I can get our app to verify using that tool. Seems like a super clever way to do it. Hopefully our move to split apk's doesn't complicate it, but we'll see :crossed_fingers:

greyson-signal avatar Jul 22 '24 13:07 greyson-signal

I will try verifying the website APK again sometime soon and report back.

By promising identical results are always generated from a given source, this allows multiple third parties to come to a consensus on a “correct” result [...]

Split APKs certainly complicate Reproducible Builds. Having only device-specific APKs instead of a single reference APK (or a handful of reference APKs when using split ABI) makes consensus on a "correct" result much harder, if not impossible, as "correct" is no longer universal but device-specific.

FYI: I cannot easily work on supporting/testing signature copying for split APKs as I have no Android devices with the play store installed to test with. Help would be appreciated :)

obfusk avatar Jul 29 '24 01:07 obfusk

I will try verifying the website APK again sometime soon and report back.

Sadly, I can't reproduce it. Though the results I get are different from before.

I've tried both github actions with the Dockerfile and instructions from this repo (as before):

  • https://github.com/obfusk/Signal-Android/actions/runs/10232972084

I get differences in classes.dex, classes3.dex, classes4.dex, classes5.dex, classes6.dex (as well as the .prof and .profm but that is expected with differences in .dex).

Except for classes6.dex they all differ only in the pg-map-id:

@@ -1,5 +1,5 @@
-00000000: 6465 780a 3033 3500 7ac7 4fe4 18f2 c5e7  dex.035.z.O.....
-00000010: 7795 ca9c 5e3c 03c2 9383 0243 a092 dddd  w...^<.....C....
+00000000: 6465 780a 3033 3500 e2c5 1d0c 9e2c 0021  dex.035......,.!
+00000010: 0475 e30c dbae 870d e95c 8ebc 3a5f dbea  .u.......\..:_..
 00000020: 04b4 a400 7000 0000 7856 3412 0000 0000  ....p...xV4.....
 00000030: 0000 0000 34b3 a400 1f25 0100 7000 0000  ....4....%..p...
 00000040: 3b2e 0000 ec94 0400 533f 0000 d84d 0500  ;.......S?...M..
@@ -582377,7 +582377,7 @@
 008e2e80: 2268 6173 2d63 6865 636b 7375 6d73 223a  "has-checksums":
 008e2e90: 6661 6c73 652c 226d 696e 2d61 7069 223a  false,"min-api":
 008e2ea0: 3231 2c22 7067 2d6d 6170 2d69 6422 3a22  21,"pg-map-id":"
-008e2eb0: 3565 3062 6331 3822 2c22 7238 2d6d 6f64  5e0bc18","r8-mod
+008e2eb0: 3932 3761 6333 3822 2c22 7238 2d6d 6f64  927ac38","r8-mod
 008e2ec0: 6522 3a22 6675 6c6c 222c 2276 6572 7369  e":"full","versi
 008e2ed0: 6f6e 223a 2238 2e34 2e32 3622 7d00 01c2  on":"8.4.26"}...
 008e2ee0: a000 01e2 808b 0001 e280 a600 02ed acbf  ................

classes6.dex has code differences too (only that small one):

@@ -142044,7 +142044,7 @@
         0x0000 - 0x0004 reg=0 this Lorg/thoughtcrime/securesms/registration/fragments/CountryPickerFragmentDirections; 
 
     #1              : (in Lorg/thoughtcrime/securesms/registration/fragments/CountryPickerFragmentDirections;)
-      name          : 'actionPopAppSettingsChangeNumber'
+      name          : 'actionRestartToWelcomeFragment'
       type          : '()Landroidx/navigation/NavDirections;'
       access        : 0x0009 (PUBLIC STATIC)
       code          -
@@ -142052,8 +142052,8 @@
       ins           : 0
       outs          : 0
       insns size    : 5 16-bit code units
-| org.thoughtcrime.securesms.registration.fragments.CountryPickerFragmentDirections.actionPopAppSettingsChangeNumber:()Landroidx/navigation/NavDirections;
-|: invoke-static {}, Lorg/thoughtcrime/securesms/AppSettingsChangeNumberDirections;.actionPopAppSettingsChangeNumber:()Landroidx/navigation/NavDirections;
+| org.thoughtcrime.securesms.registration.fragments.CountryPickerFragmentDirections.actionRestartToWelcomeFragment:()Landroidx/navigation/NavDirections;
+|: invoke-static {}, Lorg/thoughtcrime/securesms/SignupDirections;.actionRestartToWelcomeFragment:()Landroidx/navigation/NavDirections;
 |: move-result-object v0
 |: return-object v0
       catches       : (none)

As well as using rbtlog (Reproducible Builds Transparency Log for Android APKs):

  • https://github.com/obfusk/rbtlog/commit/c3cfc01725f79cfef6f62d6b6285dadb6cc38df9
  • https://github.com/obfusk/rbtlog/actions/runs/10231908976

The build environment used should be very close to identical to the docker container from this repo. It's also an ubuntu:jammy docker container -- with the same SDK versions etc. Just a more recent one and without the apt config changes.

But this has many of these odd small differences in constant values (and nothing else) in classes4.dex, classes5.dex, classes6.dex compared to the rebuild using the container from this repo, which is quite odd.

@@ -65320,7 +65320,7 @@
 |: invoke-interface/range {v19}, Landroidx/compose/runtime/Composer;.endReplaceableGroup:()V
 |: move-object v12, v3
 |: check-cast v12, Landroidx/compose/runtime/MutableState;
-|: const v3, #float 9.60821e+37 // #7e90917e
+|: const v3, #float -1.98074e-22 // #9b6f74e6
 |: invoke-interface {v11, v3}, Landroidx/compose/runtime/Composer;.startReplaceableGroup:(I)V
 |: invoke-interface {v11, v12}, Landroidx/compose/runtime/Composer;.changed:(Ljava/lang/Object;)Z
 |: move-result v3

obfusk avatar Aug 04 '24 23:08 obfusk

Looks like all those differences in constant values are calls to startReplaceableGroup() with different keys between the two builds. That suggests nondeterminism in the compose compiler :upside_down_face:

obfusk avatar Aug 05 '24 00:08 obfusk

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Oct 05 '24 18:10 stale[bot]

Please don't close this, having working and verified reproducible builds is extremely important for the entire Signal community.

otonoton avatar Nov 06 '24 02:11 otonoton

FWIW we did some additional work as part of #13754. Feel pretty confident about the build's reproducible getting back in good shape. Is there anything that you're still finding to be broken?

greyson-signal avatar Nov 06 '24 20:11 greyson-signal

Thank you for those improvements.

The scheduled reproducibility test running here on Github failed for a year before this issue was opened. Though that somehow conflicts with what you said in your first message in this issue, so I'm not sure if I missed something. My quick commit log reading didn't find a fix for now detecting the type of failure you mentioned. Was that issue with the false reproducible verdict from the test fixed?

Does the release process currently require that the reproducibility test succeeded on the to be released version? (I can't find code for a release process that ensures that.)

JanZerebecki avatar Nov 06 '24 22:11 JanZerebecki

The scheduled reproducibility test running here on Github failed for a year before this issue was opened. Though that somehow conflicts with what you said in your first message in this issue, so I'm not sure if I missed something.

That github action just checks to make sure that the app can be built at all with the docker image. I think it was failing for a while because of a configuration issue someone fixed. That action though is sorta redundant, because every release is built with that docker image. So it's more testing whether we can properly configure github actions... I may just remove it, because it's not super useful.

The reproducible checks I'm referring to are not run on Github Actions. They're run on an internal machine. Every night, it builds 3 copies of the app with the docker image and compares them with the python script we use in the reproducible build instructions. We do this for both the active release as well as our current dev branch.

Does the release process currently require that the reproducibility test succeeded on the to be released version?

No, and tbh, I'd rather not make it a strict requirement. (1) As seen in #13754, there's no silver bullet here in testing reproducible builds and (2) the only way I do know how to test (make multiple clean builds and compare them) takes a long time. And I'd rather not have the release process get even longer.

I'm happy now with the aforementioned nightly task that checks reproducibility. Nothing is perfect though, which is why we also rely on external people occasionally testing the process :)

greyson-signal avatar Nov 07 '24 16:11 greyson-signal

It seems reproducible build is still broken for recent releases. I tried to build 7.27.1 and compared with website apk and I got this:

./apkdiff/apkdiff.py Signal-Android-website-prod-universal-release-7.27.1.apk Signal-Android-website-prod-universal-release-unsigned-7.27.1.apk APKs differ on file assets/dexopt/baseline.prof! Files extracted to the mismatches/ directory. APKs differ on file assets/dexopt/baseline.profm! Files extracted to the mismatches/ directory. APKs differ on file classes.dex! Files extracted to the mismatches/ directory. APKs differ on file classes3.dex! Files extracted to the mismatches/ directory. APKs differ on file classes4.dex! Files extracted to the mismatches/ directory. APKs differ on file classes5.dex! Files extracted to the mismatches/ directory. APKs differ on file classes6.dex! Files extracted to the mismatches/ directory. APKs differ on file classes7.dex! Files extracted to the mismatches/ directory. APKs don't match!

Also the build will always fail for the first time(gradle daemon disappeared or something), the second time running the docker run command will work. I believe this is related to https://github.com/signalapp/Signal-Android/issues/13828

CaKrome avatar Dec 23 '24 00:12 CaKrome

Unfortunately looks like another non-deterministic R8 issue with the android navigation library when you have multiple actions with the same name. It's very strange, because it's machine-dependent. A single machine will produce the same APK every time, but APK's build on different machines may not be the same based on how R8 trimmed the navigation methods. It's basically another instance of #13754. My only guess as to why it happens is just some weird race-y thing that's dependent on CPU speed/available threads or something.

Regardless, we should be able to take a similar approach to resolve this.

greyson-signal avatar Jan 13 '25 18:01 greyson-signal

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Mar 15 '25 09:03 stale[bot]

Don't close

codenyte avatar Mar 15 '25 09:03 codenyte

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar May 15 '25 02:05 stale[bot]

Don't close

CaKrome avatar May 16 '25 00:05 CaKrome

Reproducible builds are not broken for us, are folks still encountering issues? Can you post the output if you are. They've been working for months on our automated checks each night.

cody-signal avatar May 16 '25 13:05 cody-signal

It's still broken for me on v7.41.3. I've attached the output of building and apkdiff-ing the apks.

signaloutput.txt

At the end you can see:

keith@comp:~/reproducible-signal$ ./apkdiff.py apks-i-built/base-master.apk    apks-from-device/base.apk
APKs differ on file AndroidManifest.xml! Files extracted to the mismatches/ directory.
APKs differ on file res/xml/splits0.xml! Files extracted to the mismatches/ directory.
APKs differ on file resources.arsc! Files extracted to the mismatches/ directory.
APKs don't match!
keith@comp:~/reproducible-signal$ ./apkdiff.py apks-i-built/base-arm64_v8a.apk apks-from-device/split_config.arm64_v8a.apk
APKs differ on file AndroidManifest.xml! Files extracted to the mismatches/ directory.
APKs don't match!
keith@comp:~/reproducible-signal$ ./apkdiff.py apks-i-built/base-xxhdpi.apk    apks-from-device/split_config.xxhdpi.apk
APKs differ on file AndroidManifest.xml! Files extracted to the mismatches/ directory.
APKs differ on file resources.arsc! Files extracted to the mismatches/ directory.
APKs don't match!
keith@comp:~/reproducible-signal$ 

kbailey4444 avatar May 16 '25 18:05 kbailey4444

Interesting! Thanks for sharing all the data. Any chance you can look at the AndroidManifest.xml's in the mismatches directory to see what's different? It'll give us some hint as to what's up hopefully.

cody-signal avatar May 19 '25 13:05 cody-signal

It looks like there are many places where the binary data is different. Here's hexdump -C of the two AndroidManifest.xml files.

first.txt second.txt

kbailey4444 avatar May 20 '25 00:05 kbailey4444

We've been looking at this issue for the last 6 months, and the behavior kbailey4444 noticed matches what we've also observed in the PlayStore version of the app (from v7.25.0 onwards). It seems to have gone unnoticed since most reproducibility checks, including obfusk's initial effort, focused on the universal/web or F-Droid version (distributed by Signal or built from source).

The TL;DR

Regarding the differences observed in v7.41.3, they stem from how PlayStore handles APK delivery and processes the app bundle Signal uploads. As far as we know, these changes are outside developer's control, arising from discrepancies in behavior between public tooling and PlayStore (the latter being a superset of the former).

This is explained by the fact that PlayStore needs to support split APK delivery.

AndroidManifest.xml

PlayStore adds several metadata values to mark that the APK came from Google and which version was delivered (likely an optimization for updates and version tracking, since PlayStore already does APK "frosting"): com.android.vending.derived.apk.id[^1], com.android.stamp.source, and com.android.stamp.type.

res/xml/splits0.xml

  • This is a (binary) XML file listing the languages the app supports. Entries look like <entry·key="<LANG>"·split="[split]"/> where LANG is the ISO639-1 code for the language and split denotes the split APK containing the associated res/values/strings.xml file.[^2]

  • Depending on app version, we see 1 to 3 differences in keys between PlayStore (iw, ji, in) and the local bundle (he, yi, id). All these languages (Hebrew/Yiddish/Indonesian) had their values changed when the standard was adopted, and while bundletool favors the updated ones, PlayStore reverts to the previous names, likely to handle edge cases for backward compatibility.[^3]

resources.arsc

  • This is a binary lookup table for mmapping resources (strings, assets, etc.) that the app uses at runtime.
  • The file uses a chunked structure containing header data followed by resource information. Binary diff between PlayStore and local builds reveals differences in the res1 header field, now renamed to ResTable_typeSpec.types_count. This previously unused field (originally reserved for struct padding/alignment) was repurposed in 2023 to optimize idmap lookups.
  • aapt2 does not set this field, but PlayStore seems to parse the table and assign it.

Note: For an example comparison of these files (for v7.25.0, which should be similar for v7.41.3), we have some diffoscope results here (the ones including "_play" in the name) that should be more human-readable than binary diffs.

Reproducibility and Validation Steps

We have been investigating this and other reproducibility-related issues in this GitHub repo: https://github.com/TheTechZone/reproducible-tests/tree/writeup

Among other things, it provides scripts for automatically validating the behavior seen here:

# clone the repo
export GIT_LFS_SKIP_SMUDGE=1
git clone --filter=blob:none https://github.com/TheTechZone/reproducible-tests.git -b writeup
cd reproducible-tests
# create a python venv
python -m venv .venv
source ./.venv/bin/activate
pip install -r requirements-jupyter.txt

## Build the application according to Signal's instructions or using our script (more on that later)
## And run apkdiff to generate the mismatches/ folder

# comparing arsc files
./comparators/arsc_compare.py <source.arsc> <target.arsc>

# comparing binary xml files (AndroidManifest.xml, splits0.xml)
./comparators/axml_compare.py <file1.xml> <file2.xml>

Aside: Automating the Reproducibility Test

We provide a script for automatically testing the reproducibility of Signal-Android. Assuming the necessary system dependencies (git, adb, docker, bundletool, etc.) are present, it handles cloning the right tag of the git repo, creating a Docker container, running the build, extracting the APK from a device via adb, and running the comparison script.

./build-signal.py --version <git tag with or without "v" prefix>

Disclaimer

This represents our current understanding of the reproducibility issues in Signal Android builds. We continue to investigate other factors that may contribute to non-reproducibility, including potential discrepancies in DEX files (not an issue for v7.42.1, but we have seen various instances of it from v7.25.0 onwards).

Our goal is to eventually achieve fully reproducible Android builds across all distribution channels. Having support from the dev team and the community would be great 😺.

[^1]: In Play Console, we expect the com.android.vending.derived.apk.id would match what can be seen in the app bundle explorer. We have only encountered derived_id=4, but that might vary.

[^2]: In the case of Signal-Android, splits are only done for the native code (i.e., libsignal) and pixel densities, so all languages are in base-master.apk.

[^3]: We suspect it might be due to an old JVM behavior (not considered a "bug" officially). The values used by PlayStore are not ISO 639-1 compliant. Update: checking the Android docs on Locale confirms this assumption. It remains unclear why the conversion is performed, since ResourceBundle.Control seems to handle any edge case during resource lookup.

TheTechZone avatar May 20 '25 10:05 TheTechZone

Hey folks, I ran through our reproducible steps and was also able to reproduce the issue. It again, unfortunately, looks like another variant of the android navigation library being non-deterministic in it's output. See my earlier comment.

The tool I usually use to debug these things is diffuse, feel free to run it on your own output apks. For split apk's it only works for the base apk, but that was enough to see the problem in this case.

Here's my output, if you're curious:

OLD: base-master.apk (signature: V1, V2, V3)
NEW: base.apk (signature: V1, V2, V3)

          │            compressed            │          uncompressed
          ├───────────┬───────────┬──────────┼──────────┬──────────┬───────────
 APK      │ old       │ new       │ diff     │ old      │ new      │ diff
──────────┼───────────┼───────────┼──────────┼──────────┼──────────┼───────────
      dex │  51.7 MiB │  51.7 MiB │   -712 B │ 51.7 MiB │ 51.7 MiB │    -712 B
     arsc │  26.4 MiB │  26.4 MiB │     +2 B │ 26.4 MiB │ 26.4 MiB │       0 B
 manifest │  15.4 KiB │  15.5 KiB │    +88 B │ 95.4 KiB │ 95.8 KiB │    +464 B
      res │   3.2 MiB │   3.2 MiB │     -1 B │  6.6 MiB │  6.6 MiB │       0 B
    asset │   3.5 MiB │   3.5 MiB │     -1 B │  4.6 MiB │  4.6 MiB │      +1 B
    other │ 784.4 KiB │ 791.1 KiB │ +6.7 KiB │  1.7 MiB │  1.7 MiB │   +17 KiB
──────────┼───────────┼───────────┼──────────┼──────────┼──────────┼───────────
    total │  85.6 MiB │  85.6 MiB │ +6.1 KiB │   91 MiB │   91 MiB │ +16.8 KiB

         │          raw           │             unique
         ├────────┬────────┬──────┼────────┬────────┬──────────────
 DEX     │ old    │ new    │ diff │ old    │ new    │ diff
─────────┼────────┼────────┼──────┼────────┼────────┼──────────────
   files │      7 │      7 │    0 │        │        │
 strings │ 370330 │ 370330 │    0 │ 304138 │ 304138 │   0 (+1 -1)
   types │  69947 │  69947 │    0 │  57286 │  57286 │   0 (+0 -0)
 classes │  53518 │  53518 │    0 │  53518 │  53518 │   0 (+0 -0)
 methods │ 348435 │ 348418 │  -17 │ 318811 │ 318794 │ -17 (+1 -18)
  fields │ 151856 │ 151856 │    0 │ 139596 │ 139596 │   0 (+0 -0)

 ARSC    │ old   │ new   │ diff
─────────┼───────┼───────┼──────
 configs │   421 │   421 │  0
 entries │ 16592 │ 16592 │  0

=================
====   APK   ====
=================

      compressed      │     uncompressed      │
───────────┬──────────┼───────────┬───────────┤
 size      │ diff     │ size      │ diff      │ path
───────────┼──────────┼───────────┼───────────┼─────────────────────────────────────────
   6.7 KiB │ +6.7 KiB │  16.7 KiB │ +16.7 KiB │ + META-INF/code_transparency_signed.jwt
   9.4 MiB │   -712 B │   9.4 MiB │    -712 B │ ∆ classes6.dex
     793 B │   -339 B │     1 KiB │    -140 B │ ∆ META-INF/BNDLTOOL.RSA
     145 B │   +145 B │      32 B │     +32 B │ + stamp-cert-sha256
 160.1 KiB │   +117 B │ 463.3 KiB │    +198 B │ ∆ META-INF/MANIFEST.MF
 165.1 KiB │    +97 B │ 463.4 KiB │    +198 B │ ∆ META-INF/BNDLTOOL.SF
  15.5 KiB │    +88 B │  95.8 KiB │    +464 B │ ∆ AndroidManifest.xml
   5.8 KiB │     -2 B │   5.7 KiB │       0 B │ ∆ assets/dexopt/baseline.profm
  26.4 MiB │     +2 B │  26.4 MiB │       0 B │ ∆ resources.arsc
    66 KiB │     +1 B │  65.9 KiB │      +1 B │ ∆ assets/dexopt/baseline.prof
   1.1 KiB │     -1 B │   8.7 KiB │       0 B │ ∆ res/xml/splits0.xml
───────────┼──────────┼───────────┼───────────┼─────────────────────────────────────────
  36.2 MiB │ +6.1 KiB │  36.9 MiB │ +16.8 KiB │ (total)


 SIGNATURES │ old                                      │ new
────────────┼──────────────────────────────────────────┼──────────────────────────────────────────
         V1 │ c9c37b71158f99dfc99b7e2f479aeef78f86a1fc │ 45989dc9ad8728c2aa9a82fa55503e34a8879374
         V2 │ c9c37b71158f99dfc99b7e2f479aeef78f86a1fc │ 45989dc9ad8728c2aa9a82fa55503e34a8879374
         V3 │ c9c37b71158f99dfc99b7e2f479aeef78f86a1fc │ 45989dc9ad8728c2aa9a82fa55503e34a8879374


======================
====   MANIFEST   ====
======================

@@ -3,6 +3,6 @@
     android:compileSdkVersionCodename="15"
+    android:requiredSplitTypes="base__abi,base__density"
+    android:splitTypes=""
     android:versionCode="154700"
     android:versionName="7.43.1"
-    nullrequiredSplitTypes="base__abi,base__density"
-    nullsplitTypes=""
     package="org.thoughtcrime.securesms"
@@ -315,3 +315,2 @@
       android:icon="@mipmap/ic_launcher"
-      android:isSplitRequired="true"
       android:label="@string/app_name"
@@ -2289,2 +2288,10 @@
     <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"
@@ -2292,2 +2299,6 @@
         />
+    <meta-data
+        android:name="com.android.vending.derived.apk.id"
+        android:value="4"
+        />
   </application>



=================
====   DEX   ====
=================

STRINGS:

   old    │ new    │ diff
  ────────┼────────┼───────────
   304138 │ 304138 │ 0 (+1 -1)

  + ~~R8{"backend":"dex","compilation-mode":"release","desugared-library-identifiers":["com.tools.android:desugar_jdk_libs_configuration:2.1.3"],"has-checksums":false,"min-api":21,"pg-map-id":"8735283","r8-mode":"full","version":"8.9.27"}

  - ~~R8{"backend":"dex","compilation-mode":"release","desugared-library-identifiers":["com.tools.android:desugar_jdk_libs_configuration:2.1.3"],"has-checksums":false,"min-api":21,"pg-map-id":"0ae2314","r8-mode":"full","version":"8.9.27"}


METHODS:

   old    │ new    │ diff
  ────────┼────────┼──────────────
   318811 │ 318794 │ -17 (+1 -18)

  + org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionRestartToWelcomeFragment() → NavDirections

  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToBackupsPreferenceFragment() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToBackupsSettingsFragment() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToChangeNumberFragment() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToChatFoldersFragment() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToCreateFoldersFragment(long, long[]) → AppSettingsDirections$ActionDirectToCreateFoldersFragment
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToCreateNotificationProfiles() → AppSettingsDirections$ActionDirectToCreateNotificationProfiles
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToDevices() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToEditProxyFragment() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToHelpFragment() → AppSettingsDirections$ActionDirectToHelpFragment
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToInviteFragment() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToManageDonations() → AppSettingsDirections$ActionDirectToManageDonations
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToNotificationProfileDetails(long) → AppSettingsDirections$ActionDirectToNotificationProfileDetails
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToNotificationProfiles() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToNotificationsSettingsFragment() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToPrivacy() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToRemoteBackupsSettingsFragment() → AppSettingsDirections$ActionDirectToRemoteBackupsSettingsFragment
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToUsernameLinkSettings() → NavDirections
  - org.thoughtcrime.securesms.preferences.EditProxyFragmentDirections actionDirectToUsernameRecovery() → AppSettingsDirections$ActionDirectToUsernameRecovery

The signature differences are expected, and we can work around the manifest stuff in the script probably, but the dex differences are navigation methods again, and it's super annoying.

I'll see if we can address these things, thank you!

greyson-signal avatar May 28 '25 17:05 greyson-signal

Using diffuse I see a subset of the same manifest changes. They look like the values that TheTechZone mentioned.

keith@comp:~/reproducible-signal$ ~/Downloads/diffuse-0.3.0/bin/diffuse diff apks-i-built/base-master.apk apks-from-device/base.apk
OLD: base-master.apk (signature: V1, V2, V3)
NEW: base.apk (signature: V1, V2, V3)

          │            compressed            │          uncompressed           
          ├───────────┬───────────┬──────────┼──────────┬──────────┬───────────
 APK      │ old       │ new       │ diff     │ old      │ new      │ diff      
──────────┼───────────┼───────────┼──────────┼──────────┼──────────┼───────────
      dex │  51.6 MiB │  51.6 MiB │      0 B │ 51.6 MiB │ 51.6 MiB │       0 B 
     arsc │  26.3 MiB │  26.3 MiB │     +2 B │ 26.3 MiB │ 26.3 MiB │       0 B 
 manifest │  15.4 KiB │  15.6 KiB │   +137 B │ 95.6 KiB │ 96.2 KiB │    +612 B 
      res │   3.2 MiB │   3.2 MiB │     -1 B │  6.6 MiB │  6.6 MiB │       0 B 
    asset │   3.5 MiB │   3.5 MiB │      0 B │  4.6 MiB │  4.6 MiB │       0 B 
    other │ 786.3 KiB │ 793.1 KiB │ +6.7 KiB │  1.7 MiB │  1.7 MiB │   +17 KiB 
──────────┼───────────┼───────────┼──────────┼──────────┼──────────┼───────────
    total │  85.4 MiB │  85.4 MiB │ +6.9 KiB │ 90.8 MiB │ 90.8 MiB │ +17.6 KiB 

         │          raw           │           unique            
         ├────────┬────────┬──────┼────────┬────────┬───────────
 DEX     │ old    │ new    │ diff │ old    │ new    │ diff      
─────────┼────────┼────────┼──────┼────────┼────────┼───────────
   files │      7 │      7 │    0 │        │        │           
 strings │ 369534 │ 369534 │    0 │ 303648 │ 303648 │ 0 (+0 -0) 
   types │  69737 │  69737 │    0 │  57140 │  57140 │ 0 (+0 -0) 
 classes │  53376 │  53376 │    0 │  53376 │  53376 │ 0 (+0 -0) 
 methods │ 347224 │ 347224 │    0 │ 317778 │ 317778 │ 0 (+0 -0) 
  fields │ 151624 │ 151624 │    0 │ 139379 │ 139379 │ 0 (+0 -0) 

 ARSC    │ old   │ new   │ diff 
─────────┼───────┼───────┼──────
 configs │   421 │   421 │  0   
 entries │ 16622 │ 16622 │  0   

=================
====   APK   ====
=================

      compressed      │     uncompressed      │                                                         
───────────┬──────────┼───────────┬───────────┤                                                         
 size      │ diff     │ size      │ diff      │ path                                                    
───────────┼──────────┼───────────┼───────────┼─────────────────────────────────────────────────────────
   6.7 KiB │ +6.7 KiB │  16.7 KiB │ +16.7 KiB │ + META-INF/code_transparency_signed.jwt                 
     794 B │   -338 B │     1 KiB │    -140 B │ ∆ META-INF/BNDLTOOL.RSA                                 
     145 B │   +145 B │      32 B │     +32 B │ + stamp-cert-sha256                                     
  15.6 KiB │   +137 B │  96.2 KiB │    +612 B │ ∆ AndroidManifest.xml                                   
 161.1 KiB │   +110 B │ 465.7 KiB │    +198 B │ ∆ META-INF/MANIFEST.MF                                  
 166.1 KiB │   +109 B │ 465.8 KiB │    +198 B │ ∆ META-INF/BNDLTOOL.SF                                  
     195 B │     +6 B │       7 B │       0 B │ ∆ META-INF/com.google.android.material_material.version 
     186 B │     -2 B │       6 B │       0 B │ ∆ META-INF/androidx.activity_activity-compose.version   
  26.3 MiB │     +2 B │  26.3 MiB │       0 B │ ∆ resources.arsc                                        
   1.1 KiB │     -1 B │   8.7 KiB │       0 B │ ∆ res/xml/splits0.xml                                   
───────────┼──────────┼───────────┼───────────┼─────────────────────────────────────────────────────────
  26.7 MiB │ +6.9 KiB │  27.4 MiB │ +17.6 KiB │ (total)                                                 


 SIGNATURES │ old                                      │ new                                      
────────────┼──────────────────────────────────────────┼──────────────────────────────────────────
         V1 │ ca6309b0bc1a8aa6bb5ebe904695ba8b18268692 │ 45989dc9ad8728c2aa9a82fa55503e34a8879374 
         V2 │ ca6309b0bc1a8aa6bb5ebe904695ba8b18268692 │ 45989dc9ad8728c2aa9a82fa55503e34a8879374 
         V3 │ ca6309b0bc1a8aa6bb5ebe904695ba8b18268692 │ 45989dc9ad8728c2aa9a82fa55503e34a8879374 


======================
====   MANIFEST   ====
======================

@@ -2298,2 +2298,10 @@
     <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"
@@ -2301,2 +2309,6 @@
         />
+    <meta-data
+        android:name="com.android.vending.derived.apk.id"
+        android:value="4"
+        />
   </application>

kbailey4444 avatar May 28 '25 19:05 kbailey4444

We believe this will be addressed in 7.44

greyson-signal avatar May 29 '25 13:05 greyson-signal

Saw v7.44 was tagged, will test it once it hits the beta channel.

TheTechZone avatar Jun 05 '25 17:06 TheTechZone

It's still broken for 7.44.2 as far as I have tried.

grep -Fr '7.44.2' .
grep: ./apks-from-device/base.apk: binary file matches
grep: ./apks-i-built/apks/splits/base-master.apk: binary file matches

ll -R mismatches/
mismatches/:
total 16
drwxr-xr-x 4 root root 4096 Jun 13 16:46 ./
drwxr-xr-x 5 root root 4096 Jun 13 16:46 ../
drwxr-xr-x 3 root root 4096 Jun 13 16:46 first/
drwxr-xr-x 3 root root 4096 Jun 13 16:46 second/

mismatches/first:
total 464
drwxr-xr-x 3 root root   4096 Jun 13 16:46 ./
drwxr-xr-x 4 root root   4096 Jun 13 16:46 ../
-rw-r--r-- 1 root root    808 Jun 13 16:47 AndroidManifest.xml
drwxr-xr-x 3 root root   4096 Jun 13 16:46 res/
-rw-r--r-- 1 root root 457916 Jun 13 16:47 resources.arsc

mismatches/first/res:
total 12
drwxr-xr-x 3 root root 4096 Jun 13 16:46 ./
drwxr-xr-x 3 root root 4096 Jun 13 16:46 ../
drwxr-xr-x 2 root root 4096 Jun 13 16:46 xml/

mismatches/first/res/xml:
total 20
drwxr-xr-x 2 root root 4096 Jun 13 16:46 ./
drwxr-xr-x 3 root root 4096 Jun 13 16:46 ../
-rw-r--r-- 1 root root 8952 Jun 13 16:46 splits0.xml

mismatches/second:
total 464
drwxr-xr-x 3 root root   4096 Jun 13 16:46 ./
drwxr-xr-x 4 root root   4096 Jun 13 16:46 ../
-rw-r--r-- 1 root root   1052 Jun 13 16:47 AndroidManifest.xml
drwxr-xr-x 3 root root   4096 Jun 13 16:46 res/
-rw-r--r-- 1 root root 457916 Jun 13 16:47 resources.arsc

mismatches/second/res:
total 12
drwxr-xr-x 3 root root 4096 Jun 13 16:46 ./
drwxr-xr-x 3 root root 4096 Jun 13 16:46 ../
drwxr-xr-x 2 root root 4096 Jun 13 16:46 xml/

mismatches/second/res/xml:
total 20
drwxr-xr-x 2 root root 4096 Jun 13 16:46 ./
drwxr-xr-x 3 root root 4096 Jun 13 16:46 ../
-rw-r--r-- 1 root root 8952 Jun 13 16:46 splits0.xml

rch7 avatar Jun 13 '25 21:06 rch7

@rch7 Based on your output, looks like the issue around dex's being mismatched has been fixed, but there's still some of this lingering XML stuff that we have to address, probably in the python diffing script.

greyson-signal avatar Jun 16 '25 14:06 greyson-signal

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Aug 15 '25 18:08 stale[bot]