diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 53188aa..ba49f2a 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -27,3 +27,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./gradlew :patches:buildAndroid --no-daemon + + - name: Upload patch artifact + uses: actions/upload-artifact@v7 + with: + name: patches-pr + path: patches/build/libs/patches-*.mpp + retention-days: 7 diff --git a/patches-list.json b/patches-list.json index 19773a5..287cf9c 100644 --- a/patches-list.json +++ b/patches-list.json @@ -48,7 +48,7 @@ ], "compatiblePackages": { "com.google.android.apps.magazines": [ - "5.161.0.931240252" + "5.163.0.947799485" ] }, "options": [] @@ -63,6 +63,7 @@ ], "compatiblePackages": { "com.google.android.apps.magazines": [ + "5.163.0.947799485", "5.161.0.931240252", "5.158.0.908428942", "5.156.0.892791979" diff --git a/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/EnableCustomTabsPatch.kt b/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/EnableCustomTabsPatch.kt index b311de3..13cf5dd 100644 --- a/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/EnableCustomTabsPatch.kt +++ b/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/EnableCustomTabsPatch.kt @@ -17,7 +17,7 @@ private val COMPAT = Compatibility( packageName = "com.google.android.apps.magazines", appIconColor = 0x4285F4, targets = listOf( - AppTarget(version = "5.161.0.931240252"), + AppTarget(version = "5.163.0.947799485"), ), ) @@ -32,48 +32,48 @@ val enableCustomTabsPatch = bytecodePatch( execute { with(InstructionExtensions) { - // Step 1: Patch Ladvb.a() — replace IF_EQZ experiment flag check with NOP. - // Ladvb.a() reads Laqrp experiment flag and returns the disabled Ladvf picker - // when the flag is OFF. With NOP, it always returns the enabled Ladvi picker. - val advbMethod = LadvbFingerprint.method + // Step 1: Patch Laeah.a() — replace IF_EQZ experiment flag check with NOP. + // Laeah.a() reads Lardw experiment flag and returns the disabled Laeal picker + // when the flag is OFF. With NOP, it always returns the enabled Laeao picker. + val laeahMethod = LaeahFingerprint.method var ifEqzIndex = -1 - for ((i, instr) in advbMethod.implementation!!.instructions.withIndex()) { + for ((i, instr) in laeahMethod.implementation!!.instructions.withIndex()) { if (instr.opcode == Opcode.IF_EQZ) { ifEqzIndex = i break } } - check(ifEqzIndex != -1) { "IF_EQZ not found in Ladvb.a()" } - advbMethod.replaceInstruction(ifEqzIndex, "nop") + check(ifEqzIndex != -1) { "IF_EQZ not found in Laeah.a()" } + laeahMethod.replaceInstruction(ifEqzIndex, "nop") - // Step 2: Bypass experiment allowlist in Ladvi.a() (enabled browser picker). - // Ladvi.a() resolves the OS default browser, then checks if it's in the experiment + // Step 2: Bypass experiment allowlist in Laeao.a() (enabled browser picker). + // Laeao.a() resolves the OS default browser, then checks if it's in the experiment // allowlist. NOP-ing the IF_EQZ makes it always return whatever the OS resolved. - LadviFingerprint.methodOrNull?.let { method -> + LaeaoFingerprint.methodOrNull?.let { method -> val idx = method.implementation!!.instructions.indexOfFirst { it.opcode == Opcode.IF_EQZ } if (idx != -1) method.replaceInstruction(idx, "nop") } - // Step 3: Bypass allowlist-only filtering in Ladvf.a() (disabled picker, belt+suspenders). - // Ladvf.a() returns null when no installed browser matches the allowlist. NOP-ing the + // Step 3: Bypass allowlist-only filtering in Laeal.a() (disabled picker, belt+suspenders). + // Laeal.a() returns null when no installed browser matches the allowlist. NOP-ing the // IF_EQZ always proceeds to compute a candidate instead of bailing out early. - LadvfFingerprint.methodOrNull?.let { method -> + LaealFingerprint.methodOrNull?.let { method -> val idx = method.implementation!!.instructions.indexOfFirst { it.opcode == Opcode.IF_EQZ } if (idx != -1) method.replaceInstruction(idx, "nop") } - // Step 4: In every method that reads Ladut.i, replace iget-boolean Ladut;->i:Z + // Step 4: In every method that reads Ladzz.i, replace iget-boolean Ladzz;->i:Z // with const/4 vX, 0x1 so the CustomTabs branch is always taken. val methods = listOf( - LajdqFingerprint.method, - LadwcFingerprint.methodOrNull, - LajdxFingerprint.methodOrNull, - LajheFingerprint.methodOrNull, - LajhlFingerprint.methodOrNull, + LajpvFingerprint.method, + LajtqFingerprint.methodOrNull, + LajtjFingerprint.methodOrNull, + LaebiFingerprint.methodOrNull, + LajqcFingerprint.methodOrNull, ).filterNotNull() for (method in methods) { @@ -82,7 +82,7 @@ val enableCustomTabsPatch = bytecodePatch( for (instr in method.implementation!!.instructions) { if (instr.opcode == Opcode.IGET_BOOLEAN) { val ref = (instr as ReferenceInstruction).reference - if (ref is FieldReference && ref.definingClass == "Ladut;" && ref.name == "i") { + if (ref is FieldReference && ref.definingClass == "Ladzz;" && ref.name == "i") { targets.add(index) } } @@ -94,8 +94,8 @@ val enableCustomTabsPatch = bytecodePatch( } } - // Step 5: CustomTabsTrampolineActivity.onCreate() reads field a (Ladvd), calls - // Ladvd.a(), and does an IF_NEZ on the (possibly null) result; if null, it logs + // Step 5: CustomTabsTrampolineActivity.onCreate() reads field a (Laeaj), calls + // Laeaj.a(), and does an IF_NEZ on the (possibly null) result; if null, it logs // "Unexpected intent; activity is not enabled" and finishes. Convert the IF_NEZ into // an unconditional GOTO to its existing target so the enabled path is always taken. val trampolineMethod = CustomTabsTrampolineFingerprint.method @@ -104,7 +104,7 @@ val enableCustomTabsPatch = bytecodePatch( val ref = (it as ReferenceInstruction).reference ref is FieldReference && ref.definingClass == "Lcom/google/apps/dots/android/modules/reading/customtabs/CustomTabsTrampolineActivity;" && - ref.name == "a" && ref.type == "Ladvd;" + ref.name == "a" && ref.type == "Laeaj;" } check(fieldReadIndex != -1) { "Field read of CustomTabsTrampolineActivity.a not found" } diff --git a/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/Fingerprints.kt b/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/Fingerprints.kt index 3253584..88107b0 100644 --- a/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/Fingerprints.kt +++ b/patches/src/main/kotlin/app/docbt/patched_up/googlenews/customtabs/Fingerprints.kt @@ -4,78 +4,78 @@ import app.morphe.patcher.Fingerprint import app.morphe.patcher.fieldAccess import com.android.tools.smali.dexlib2.Opcode -// All methods in Google News v5.161.0 that read Ladut;->i:Z (iget-boolean). -// Ladut = ArticleLauncher; field i controls CustomTabs (true) vs WebView (false). +// All methods in Google News v5.163.0 that read Ladzz;->i:Z (iget-boolean). +// Ladzz = ArticleLauncher; field i controls CustomTabs (true) vs WebView (false). // // Verified read sites (DEX bytecode scan): -// classes.dex: Lajdq (handler, method D, two read sites) -// classes3.dex: Ladwc (handler) -// Lajdx (handler) -// Lajhe (handler) -// Lajhl (handler) +// classes.dex: Lajpv (handler, two read sites) +// classes3.dex: Lajtq (handler) +// Lajtj (handler) +// Laebi (handler) +// Lajqc (handler) -internal object LajdqFingerprint : Fingerprint( - filters = listOf(fieldAccess("Ladut;", "i", "Z", Opcode.IGET_BOOLEAN)), - custom = { _, classDef -> classDef.type == "Lajdq;" }, +internal object LajpvFingerprint : Fingerprint( + filters = listOf(fieldAccess("Ladzz;", "i", "Z", Opcode.IGET_BOOLEAN)), + custom = { _, classDef -> classDef.type == "Lajpv;" }, ) -internal object LadwcFingerprint : Fingerprint( - filters = listOf(fieldAccess("Ladut;", "i", "Z", Opcode.IGET_BOOLEAN)), - custom = { _, classDef -> classDef.type == "Ladwc;" }, +internal object LajtqFingerprint : Fingerprint( + filters = listOf(fieldAccess("Ladzz;", "i", "Z", Opcode.IGET_BOOLEAN)), + custom = { _, classDef -> classDef.type == "Lajtq;" }, ) -internal object LajdxFingerprint : Fingerprint( - filters = listOf(fieldAccess("Ladut;", "i", "Z", Opcode.IGET_BOOLEAN)), - custom = { _, classDef -> classDef.type == "Lajdx;" }, +internal object LajtjFingerprint : Fingerprint( + filters = listOf(fieldAccess("Ladzz;", "i", "Z", Opcode.IGET_BOOLEAN)), + custom = { _, classDef -> classDef.type == "Lajtj;" }, ) -internal object LajheFingerprint : Fingerprint( - filters = listOf(fieldAccess("Ladut;", "i", "Z", Opcode.IGET_BOOLEAN)), - custom = { _, classDef -> classDef.type == "Lajhe;" }, +internal object LaebiFingerprint : Fingerprint( + filters = listOf(fieldAccess("Ladzz;", "i", "Z", Opcode.IGET_BOOLEAN)), + custom = { _, classDef -> classDef.type == "Laebi;" }, ) -internal object LajhlFingerprint : Fingerprint( - filters = listOf(fieldAccess("Ladut;", "i", "Z", Opcode.IGET_BOOLEAN)), - custom = { _, classDef -> classDef.type == "Lajhl;" }, +internal object LajqcFingerprint : Fingerprint( + filters = listOf(fieldAccess("Ladzz;", "i", "Z", Opcode.IGET_BOOLEAN)), + custom = { _, classDef -> classDef.type == "Lajqc;" }, ) -// Ladvb.a(Laqxf, Laqxf) — experiment flag switch. -// Reads Laqrp;->a:Laqrp; (experiment flag), calls .a().f() → boolean. -// IF_EQZ branches to the disabled Ladvf picker when the flag is OFF. -// NOP-ing the IF_EQZ forces the enabled Ladvi path (resolveActivity-based browser picker). -internal object LadvbFingerprint : Fingerprint( - filters = listOf(fieldAccess("Laqrp;", "a", "Laqrp;", Opcode.SGET_OBJECT)), - custom = { _, classDef -> classDef.type == "Ladvb;" }, +// Laeah.a(Larjq, Larjq) — experiment flag switch. +// Reads Lardw;->a:Lardw; (experiment flag), calls .a().f() → boolean. +// IF_EQZ branches to the disabled Laeal picker when the flag is OFF. +// NOP-ing the IF_EQZ forces the enabled Laeao path (resolveActivity-based browser picker). +internal object LaeahFingerprint : Fingerprint( + filters = listOf(fieldAccess("Lardw;", "a", "Lardw;", Opcode.SGET_OBJECT)), + custom = { _, classDef -> classDef.type == "Laeah;" }, ) -// Ladvi.a() — "enabled" Ladvd impl: resolves the OS default browser via Leu.a(...), then checks -// if it's in the experiment allowlist (Laqrp). IF_EQZ skips the immediate return when the default +// Laeao.a() — "enabled" Laeaj impl: resolves the OS default browser via Lew.a(...), then checks +// if it's in the experiment allowlist (Lardw). IF_EQZ skips the immediate return when the default // browser isn't allowlisted (e.g. Firefox/Brave), falling back to an allowlist-only candidate list. // NOP-ing it always returns the OS default browser, regardless of the allowlist. -internal object LadviFingerprint : Fingerprint( - filters = listOf(fieldAccess("Ladvi;", "a", "Landroid/content/Context;", Opcode.IGET_OBJECT)), - custom = { _, classDef -> classDef.type == "Ladvi;" }, +internal object LaeaoFingerprint : Fingerprint( + filters = listOf(fieldAccess("Laeao;", "a", "Landroid/content/Context;", Opcode.IGET_OBJECT)), + custom = { _, classDef -> classDef.type == "Laeao;" }, ) -// Ladvf.a() — "disabled" Ladvd impl: filters installed browsers against the experiment allowlist +// Laeal.a() — "disabled" Laeaj impl: filters installed browsers against the experiment allowlist // only (no default-browser fallback), returns null if the filtered list is empty. IF_EQZ returns // null in that case. NOP-ing it always proceeds to compute a candidate from the (possibly empty) // filtered list instead of bailing out early. -internal object LadvfFingerprint : Fingerprint( - filters = listOf(fieldAccess("Ladvf;", "a", "Landroid/content/Context;", Opcode.IGET_OBJECT)), - custom = { _, classDef -> classDef.type == "Ladvf;" }, +internal object LaealFingerprint : Fingerprint( + filters = listOf(fieldAccess("Laeal;", "a", "Landroid/content/Context;", Opcode.IGET_OBJECT)), + custom = { _, classDef -> classDef.type == "Laeal;" }, ) -// CustomTabsTrampolineActivity.onCreate() reads field a (Ladvd) and calls a()Ljava/lang/String; +// CustomTabsTrampolineActivity.onCreate() reads field a (Laeaj) and calls a()Ljava/lang/String; // then does if-nez on the result; if null, it logs "Unexpected intent; activity is not enabled" // and finishes. Patch the if-nez into an unconditional goto to its existing target so the -// enabled path is always taken regardless of what Ladvd.a() returns. +// enabled path is always taken regardless of what Laeaj.a() returns. internal object CustomTabsTrampolineFingerprint : Fingerprint( filters = listOf( fieldAccess( "Lcom/google/apps/dots/android/modules/reading/customtabs/CustomTabsTrampolineActivity;", "a", - "Ladvd;", + "Laeaj;", Opcode.IGET_OBJECT, ), ), diff --git a/patches/src/main/kotlin/app/docbt/patched_up/googlenews/gms/GmsCoreSupportPatch.kt b/patches/src/main/kotlin/app/docbt/patched_up/googlenews/gms/GmsCoreSupportPatch.kt index ef1a963..82938d2 100644 --- a/patches/src/main/kotlin/app/docbt/patched_up/googlenews/gms/GmsCoreSupportPatch.kt +++ b/patches/src/main/kotlin/app/docbt/patched_up/googlenews/gms/GmsCoreSupportPatch.kt @@ -23,6 +23,7 @@ private val COMPAT = Compatibility( packageName = FROM_PACKAGE, appIconColor = 0x4285F4, targets = listOf( + AppTarget(version = "5.163.0.947799485"), AppTarget(version = "5.161.0.931240252"), AppTarget(version = "5.158.0.908428942"), AppTarget(version = "5.156.0.892791979"), @@ -110,7 +111,13 @@ private val ACTIONS = setOf( "com.google.android.gms.asterism.service.START", "com.google.android.gms.audiomodem.service.AudioModemService.START", "com.google.android.gms.audit.service.START", - "com.google.android.gms.auth.aang.events.services.START", + // NOTE: com.google.android.gms.auth.aang.events.services.START is deliberately + // NOT transformed. The AANG events service (bound via getStartServiceAction() in + // the aang auth client) has no counterpart in MicroG RE, so rewriting it to + // "app.revanced.*" points the bind at a non-existent service and breaks the + // fresh-install account-add return flow (sign-in loops after login). Leaving it + // as "com.google.*" restores the behaviour of the first working releases. + // See regression from commit 7342da2. "com.google.android.gms.auth.account.authapi.START", "com.google.android.gms.auth.account.authenticator.auto.service.START", "com.google.android.gms.auth.account.authenticator.chromeos.START", diff --git a/versions.json b/versions.json index f985ba1..daec0c3 100644 --- a/versions.json +++ b/versions.json @@ -1,4 +1,4 @@ { - "googleNews": "5.161.0.931240252", + "googleNews": "5.163.0.947799485", "kleinanzeigen": "2026.16.1" }