Upstream - #18
Open
superturtlee wants to merge 6 commits into
Open
Conversation
Gradle 9.6 removed the internal API that AGP 8.13 relied on, forcing the AGP 9 upgrade and the surrounding build-script changes. Toolchain: - Gradle wrapper 9.3.1 -> 9.6.1; AGP 8.13.1 -> 9.3.1; apksig follows AGP. - Adopt AGP 9 built-in Kotlin: remove org.jetbrains.kotlin.android from all Android modules; Kotlin stdlib 2.3.10 -> 2.4.10. - compileSdk/targetSdk 36 -> 37, build-tools 37.0.0. Build-script migration: - Root build configures the shared CommonExtension through its getters, since AGP 9 dropped the action-DSL methods on that type. - daemon generates SignInfo through androidComponents.onVariants and a typed task; android.applicationVariants was removed. Resource generators (replace the rikka autoResConfig / materialthemebuilder plugins, whose entry points use removed AGP variant APIs): - buildSrc/GenerateLangListTask scans the translated locales. - buildSrc/GenerateMaterialThemeTask computes the accent-color theme overlays, reusing the materialthemebuilder color library without applying its plugin. Drop android.nonFinalResIds=false: AGP 9 enables optimized resource shrinking by default, and that shrinker requires non-final resource IDs. With isShrinkResources=true the manager's release build now fails :app:minifyReleaseWithR8 with "Optimized resource shrinking requires non-final IDs". AGP offers two remedies: make the IDs non-final, or opt out of optimized shrinking (r8.optimizedResourceShrinking= false). We take the former, because the false setting turns out to be dead weight: - It was added in 348f049 (Aug 2023), an unrelated "show packagename" feature commit, as a one-line drop-in beside the now-removed experimental flags enableAppCompileTimeRClass / enableNewResourceShrinker.preciseShrinking. Those siblings were cleaned up later; this line was simply missed. - Final IDs are only actually required to use R.* as Java switch/case labels. There are zero `case R.*` occurrences in the tree at 348f049 and at every commit since, so the flag never protected anything here. - Non-final IDs are the modern AGP default, so removing the line (rather than writing =true) expresses the intent with no config at all. It also builds smaller: the optimized shrinker trims the release APK from ~3.45 MB to ~3.13 MB (~9%). Verified end to end -- assembleRelease plus a zygisk installKsuAndReboot run that loads the module and starts lspd on device. Formatting task: - Add buildSrc/src/main/kotlin to the format task so the generator sources are formatted with the rest of the Kotlin build logic. - Exclude daemon/**, which is intentionally kept on ktfmt's default (Meta) style; formatting it here fought :daemon:ktfmtFormat and flipped the style back and forth. Dependencies: - AGP/apksig 9.3.1, Kotlin 2.4.10, androidx.core 1.19.0 (dependabot maven group). - coroutines 1.11.0, okhttp 5.4.0, gson 2.14.0, nav 2.9.8, glide 5.0.9, androidx activity/browser/annotation, ktfmt 0.26.0. - Material kept at 1.12.0; 1.13+ removes the colorPrimary/colorError attrs the manager references. - Submodules fmt and commons-lang bumped; CI action versions bumped (actions/checkout 6 -> 7, actions/cache 5 -> 6).
Android 17 (API 37) reshaped the IServiceConnection callback and dropped the
old overload instead of keeping both:
void connected(in ComponentName name, IBinder service,
in @nullable IBinderSession session, boolean dead);
ManagerGuard overrode only the three-argument form, so as soon as system_server
dispatched the new transaction the Stub landed on an abstract method and the
daemon died with AbstractMethodError, taking the manager session down with it.
Only the Xiaomi XSpace workaround binds this connection, which is why the crash
was reported on HyperOS first; the interface change itself ships in stock
Android 17 and is not vendor specific.
Declare both overloads in the IServiceConnection stub and override both in
ManagerGuard, so every supported release finds the method system_server
dispatches. IBinderSession is stubbed as an empty interface, since the type is
only referenced by the descriptor of the new overload.
Brings the framework in line with the API version master vendors, checked throughout with test modules that assert the documented behaviour and log pass/fail, on a Pixel 6 running Android 17. hookClassInitializer never worked: it aborted the process, and once that was fixed the hook still could not fire, because resolving <clinit> through JNI runs the initialiser during the lookup. It finds the method from ART's layout now — the gap the reflected members leave behind — and the hooker runs ahead of the class's own initialiser. Also fixed: the interceptor chain could resurrect an exception it had already suppressed; ExceptionMode.DEFAULT ignored module.prop, so passthrough was unreachable; getInvoker() threw NPE on an unhooked method; Constructor .newInstance was hookable; an unhooked constructor dispatched Method.invoke's id against itself; getArgs() was mutable; a late-injected system server dispatched onSystemServerStarting into an empty module set; android:ui was reported to modules as the system package; edit().clear() did nothing, and preference updates reached every Android user's hooked processes; empty scope requests never called back; openRemoteFile threw RemoteException where FileNotFoundException is documented; getScope() repeated a package once per user; module.prop was not parsed as Properties. In the manager, one malformed module.prop could blank the entire module list. The list now shows which API each module targets, and staticScope is enforced rather than parsed and ignored — in the picker, in the daemon, and by dropping stale rows at startup. Two changes modules will notice: - Invoker.invoke reports the target's exception wrapped in InvocationTargetException on every path, as Method#invoke does. A module catching the raw exception will stop catching it. - A module declaring staticScope loses scope entries outside its scope.list. The <clinit> lookup is measured on one device, one Android version and one architecture. Its assumptions are re-checked at runtime and it declines rather than guesses, so an unfamiliar layout degrades to "no static initializer" rather than misbehaving.
Since #648 the `ServiceManager.addService` call that claims the `serial` name sat inside the `SDK_INT >= R` branch that exists only for `registerForNotifications`, so on pre-R nothing claimed it and the Zygisk module aborted the injection. Keep only `registerForNotifications` behind the check. `startActivityAsUserWithFeature` is also R-only, and both call sites used it unconditionally; the `NoSuchMethodError` escaped `onTransact` and killed the daemon. Route them through `startActivityAsUserCompat`, like `registerReceiverCompat`. Closes #773.
An Actions artifact cannot be downloaded without a GitHub account: measured against this repository, `GET /actions/artifacts/<id>/zip` answers 401 to an anonymous caller while a release asset answers 206. Testing a canary is the lowest-friction way for an ordinary user to help, and asking each of them to grant an OAuth app something first — to work around where the zips happen to live — is a real cost for a project whose users are careful about what they install. It also excludes the users who cannot reach GitHub's login page at all, who are exactly the ones a canary programme loses first. Each push to master, and each manual run, now attaches the same two zips to a `canary-<versionCode>` prerelease. Prerelease, so `releases/latest` — which is what update checks read — keeps pointing at the last stable tag. Artifacts stay: they also carry mappings and symbols, which are for us rather than for testers. Five are kept. They are pruned by version code rather than by date, because the version code is the commit count and therefore monotonic, while dates can be disordered by a rerun or a revert. The release is deleted and recreated rather than edited, so re-running the workflow for a commit replaces that build instead of appending a second copy of every asset to it. The job gains `contents: write`, which it did not have; everything else in it only reads.
The parasitic manager lives inside com.android.shell, which has no INTERNET permission before Android 12, so preAppSpecialize appends the INET group to its gid array. That satisfies setgroups() and nothing else: once nativeForkAndSpecialize returns, Zygote#forkAndSpecialize runs setAllowNetworkingForProcess(containsInetGid(gids)) against the array it passed in, not the one we substituted, and turns networking off in libnetd_client. socket() and dns_open_proxy() then return EPERM whatever groups we belong to, which libcore reports as "Permission denied (missing INTERNET permission?)" — an unchecked SecurityException that killed the OkHttp dispatcher and the manager with it, and left the Repository tab empty. So overwrite the first entry of the caller's array too; it has no further use once specialization is done. LSPosed did this from the commit that introduced the parasitic manager, and the line was dropped in the rewrite for the new Zygisk architecture. Android 12 and later were never affected: com.android.shell declares INTERNET there, so the array already carries the group. Fixes #636
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.
No description provided.