Keep XResources out of the classes R8 shares, and stamp saved archives - #851
Open
JingMatrix wants to merge 2 commits into
Open
Keep XResources out of the classes R8 shares, and stamp saved archives#851JingMatrix wants to merge 2 commits into
JingMatrix wants to merge 2 commits into
Conversation
Some framework types extend super classes that no dex contains: they are generated at runtime, so that they can inherit from whichever platform classes the device actually provides. A type whose super class does not yet exist cannot be resolved, and the runtime records the failure rather than retrying it, so the damage outlives the window that caused it. The fragility is transitive -- every class naming such a type acquires it. A whole-program optimiser spreads it beyond what the source shows. Each lambda is lowered to a class of its own, and classes of the same shape are merged afterwards, so a lambda written in a fragile class can end up sharing its class with lambdas from anywhere in the program. The reference then sits in a class that arbitrary code instantiates, and the relationship exists only in the optimiser's output. That is what happened to XResources: its two lambdas put it inside the shared Function synthetic, which commons-lang's ClassUtilsX instantiates from its static initialiser, which XposedHelpers.findClass calls. Every findClass in system_server then failed for the rest of the boot, so no module's hooks landed there (#847, #848). Both lambdas are written out long-hand now. Keep rules cannot state the invariant, since they govern the classes one writes rather than the ones an optimiser invents, so it is checked where it is decided. checkXResourcesIsolationRelease reads the optimised dex, resolves class names through the mapping file, and fails the build if any class outside resource hooking has come to name one of these types.
Three places produced one and named it three ways: the log panel and the troubleshooting page formatted a translated string resource, and the root export built its own name inline. They now share logArchiveName(), which puts the build type in front of the stamp -- a report from a debug build explains behaviour a release build does not have, and the file name can say so without a round trip to ask. The name is no longer a string resource. It was translated into nineteen locales, where the only thing the translations could do was disagree. The extension stays a parameter: the manager writes a zip through SAF, while the root path shells out to tar, which is what Android ships. An archive now also records which commit wrote it, since the version code is the commit count on master and every branch build at the same depth wears the number of an official build it was never made from. Each format carries it where that format allows: the log zip in its comment field, the module backup as a field of its own document, since gzip's comment is not reachable through GZIPOutputStream. tar has no such slot, so the root export still says only what its name says.
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Modules loaded but none of their hooks landed in release builds, while debug builds were fine (#847, #848).
Some framework types extend super classes that no dex contains: they are generated on the device, so they can inherit from whichever platform classes it provides. Such a type cannot be resolved until that has happened, and a failed resolution is permanent, so the fragility is transitive — every class naming one acquires it.
R8 spread it beyond what the source shows. Each lambda becomes a class, and classes of the same shape are merged afterwards, so
XResources' two lambdas put it inside the sharedFunctionsynthetic — the one commons-lang'sClassUtilsXinstantiates from its static initialiser, whichXposedHelpers.findClasscalls. EveryfindClassin system_server failed from then on.Both lambdas are now written out long-hand. Keep rules cannot state this invariant, since they govern the classes you write rather than the ones an optimiser invents, so
checkXResourcesIsolationReleasereads the optimised dex, resolves names through the mapping file, and fails the build if any class outside resource hooking comes to name one of these types.Measured on an SM-A145R (Android 15, KernelSU) with NoWakeLock 3.0.10, same scope both sides:
xposed.dummy.XResourcesSuperClassfailuresSaved bug reports also get one name, built in one place instead of three. Two archives attached to the same report used to be indistinguishable until opened, so deciding which to inspect first meant extracting both; the name says the build type now, and the logs zip records the commit in its comment — the version code is the commit count on master, so branch builds wear numbers they were never built from.