Skip to content

Send a patched app's Google Play services calls to a replacement - #127

Merged
JingMatrix merged 2 commits into
masterfrom
gms-replacement
Oct 23, 2026
Merged

JingMatrix merged 2 commits into
masterfrom
gms-replacement

Conversation

@JingMatrix

@JingMatrix JingMatrix commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Supersedes #125; its author and the NPatch authors it credited are co-authors here. The run-time redirect comes from NPatch, where NkBe introduced it in 7723mod/NPatch@e640da4.

A patched app is re-signed, so Google Play services refuses it wherever it checks the caller's certificate, most visibly at sign-in. A replacement answering the same APIs under its own package can stand in, if the app's calls reach it and it knows the original certificate.

No vendor is named: a replacement is any package shaped <vendor>.android.gms, and that name is what the patch records. The manager discovers what is installed, and anything else can be typed.

Choosing one rewrites the package of GMS-bound intents and provider calls at run time, renames the app's C2DM names in the manifest permanently, and declares the original certificate in the metadata the replacement reads. Actions are deliberately left alone: a replacement answers Google's own action names under its own package, so rewriting them would send calls to names nothing declares.

Declaring the certificate alone, for a ROM with signature spoofing to report, is the alternative for a replacement installed as Google Play services itself. Either can be turned off, and choosing neither changes nothing.

Also fixes a bottom sheet whose text field sat under the keyboard.

A text field on a modal bottom sheet sat under the keyboard the moment it was
focused, taking its supporting text with it -- the validation message and the
availability hint, which are exactly what a reader needs while typing -- and a
sheet shortened by the keyboard had no way to scroll to what it had lost.

Three things were missing and all three are needed: the activity never declared
adjustResize, so no window inset was reported at all; the sheet consumes the
window insets, so imePadding in its content had nothing left to apply and the
inset has to be passed through; and the body needs to scroll once the keyboard
has taken half the screen.

They live in SheetBody, along with the language override every sheet was already
re-applying by hand, so the next sheet gets all of them without knowing about
them. Not for a sheet whose content scrolls itself: nesting a scrollable in
another of the same direction is an error, and such a sheet handles the inset
alone.

ChoiceCard moves out of the keystore sheet unchanged, so the sheets that offer a
small set of alternatives keep reading alike.

Note that adjustResize reaches every screen, not only these sheets.
@JingMatrix
JingMatrix force-pushed the gms-replacement branch 2 times, most recently from 49bbd72 to 0ad4b6a Compare October 23, 2026 13:58
@JingMatrix
JingMatrix marked this pull request as draft October 23, 2026 14:18
@JingMatrix
JingMatrix marked this pull request as ready for review October 23, 2026 14:21
@JingMatrix
JingMatrix force-pushed the gms-replacement branch 9 times, most recently from 31b0717 to eb35c88 Compare October 23, 2026 16:24
A patched app is re-signed, so Google Play services refuses it wherever it
checks the caller's certificate -- most visibly at sign-in, which fails with
UNREGISTERED_ON_API_CONSOLE. A replacement answering the same APIs under its own
package can stand in, but only if the app's calls reach it and it knows which
certificate the app was originally signed with.

Nothing here names a vendor. A replacement is any package ending in .gms other
than Google's own, and the single question asked of that name is whether it also
ends in .android.gms: such a build renamed Google's namespaces into its own so
it can be installed beside Google Play services, while one that keeps Google's
names is meant to replace it outright and serves them under those names. Only
the first needs its provider authorities and push names rewritten. The offers in
the manager are discovered by asking the device which packages answer the
sign-in service, and a package that is not among them can still be typed.

Choosing one is three changes, not a flag, which is why the sheet says so before
the patch rather than after. GmsRedirector rewrites the package, component and
provider authority of GMS-bound intents and provider calls at run time. Actions
are deliberately left alone: a replacement declares Google's own action names
under its own package, so rewriting the package is what routes a call, and
rewriting the action too would address a name nothing declares. Push is the one
exception, because the app's push receiver only accepts a sender holding a
signature permission of Google Play services that no other build can hold, so
its C2DM names are renamed in the manifest for good and the same prefix rule is
applied to the actions the app sets at run time. The original certificate is
then declared in the metadata the replacement reads.

The client library also asks the package manager -- not an intent -- whether
Google Play services is present, so the redirect never sees that question, and
on a device where it was replaced rather than supplemented the answer is no and
the library stops before sending anything. Once the chosen replacement is found
installed, isGooglePlayServicesAvailable is answered on its behalf, which is
what makes these APIs reachable at all on a device with no Google Play services.
It is logged the first time it changes an answer. Only the library's public
entry points are answered; an API that repeats the check inside its own
internals, as the advertising-id client does, still refuses.

Declaring that certificate for a ROM with signature spoofing to report is the
alternative, for a replacement that installs as Google Play services itself and
so needs no redirect. The two are alternatives rather than additions -- both
declare the same certificate and differ only in who reads it -- so at most one
is chosen, and choosing neither changes nothing.

The redirect only arms itself if the app can see the chosen package, which its
own queries do not cover; a local-mode app already holds QUERY_ALL_PACKAGES, and
the manager offers to add it to an integrated one.

Supersedes #115 and #125. The run-time redirect is ported from NPatch, where
NkBe introduced it as 7723mod/NPatch e640da4; its authors are credited below.

Co-Authored-By: Martin <kuomartin715@gmail.com>
Co-Authored-By: MrZhongzq <108169409+mrzhongzq@users.noreply.github.com>
Co-Authored-By: NkBe <HSSkyBoy@outlook.com>
@JingMatrix
JingMatrix merged commit bbd4be9 into master Oct 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant