Replace Play Integrity Fix with LSPosed + honest integrity copy#59
Conversation
Cut the PIF feature (never merged): on BlueStacks a green Play Integrity verdict isn't reachable — Google gates emulator integrity to MEETS_VIRTUAL_INTEGRITY, exclusive to Google Play Games — so a PIF button implied a capability the platform can't deliver. Confirmed empirically (root hidden + fresh fingerprint + fresh security patch + clean GMS still returns no verdict) and by the competition (no BlueStacks rooter claims it). Instead add LSPosed, which actually works on our stack: - lsposed_payload.py: pinned, hash-locked LSPosed v1.9.2 (7024) zygisk variant, download-at-runtime, mirrors the ReZygisk payload. - Magisk tab: "Install LSPosed (Xposed)" button (fetch -> install_module), gated on the manager like ReZygisk; needs ReZygisk (Zygisk) first. - Rewrite the module note to stop claiming Basic/Device are "within reach": these modules give root, Zygisk, and Xposed — not Play Integrity, which does not pass on an emulator. - README: drop "Play Integrity Fix" from the module lists; add the honest integrity caveat. 155 tests green.
There was a problem hiding this comment.
Code Review
This pull request adds support for downloading, verifying, and installing the LSPosed (Zygisk) module on BlueStacks instances, including UI integration and unit tests. The reviewer recommends replacing urllib.request.urlretrieve with urllib.request.urlopen and an explicit timeout to prevent potential hanging on unstable network connections, along with corresponding updates to the unit tests to mock urlopen instead of urlretrieve.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| _p("Downloading LSPosed (%s)..." % MODULE_NAME) | ||
| tmp = dest + ".part" | ||
| try: | ||
| urllib.request.urlretrieve(MODULE_URL, tmp) # pinned URL, hash-checked below |
There was a problem hiding this comment.
Using urllib.request.urlretrieve without a timeout can cause the background thread to hang indefinitely if the network connection is unstable or the server is unresponsive. It is highly recommended to use urllib.request.urlopen with an explicit timeout parameter instead, reading the response in chunks to write to the temporary file.
with urllib.request.urlopen(MODULE_URL, timeout=30) as response:
with open(tmp, "wb") as f:
for chunk in iter(lambda: response.read(1 << 20), b""):
f.write(chunk)|
|
||
| def _boom(*a, **k): | ||
| raise AssertionError("fetch_module downloaded despite a valid cached file") | ||
| monkeypatch.setattr(lp.urllib.request, "urlretrieve", _boom) |
There was a problem hiding this comment.
Since we updated fetch_module to use urllib.request.urlopen instead of urlretrieve, we should update this mock to target urlopen to ensure the test continues to pass.
| monkeypatch.setattr(lp.urllib.request, "urlretrieve", _boom) | |
| monkeypatch.setattr(lp.urllib.request, "urlopen", _boom) |
| def fake_dl(url, dest): | ||
| with open(dest, "wb") as f: | ||
| f.write(good) | ||
| monkeypatch.setattr(lp.urllib.request, "urlretrieve", fake_dl) |
| def _fake_download(url, dest): | ||
| with open(dest, "wb") as f: | ||
| f.write(b"corrupted-or-tampered") | ||
| monkeypatch.setattr(lp.urllib.request, "urlretrieve", _fake_download) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d13c5c4305
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| self.manager_button.setVisible(installed and not manager) | ||
| self.remove_manager_button.setVisible(manager) | ||
| self.rezygisk_button.setVisible(manager) | ||
| self.lsposed_button.setVisible(manager) |
There was a problem hiding this comment.
Hide LSPosed on unsupported Android 7 instances
The pinned LSPosed v1.9.2 supports Android 8.1–14, but this exposes the install action solely based on manager presence. For the Nougat 32/64 instances that this project explicitly supports, clicking it downloads and attempts to flash a module that cannot run on Android 7. Gate the action using the instance Android version, or reject those instances with a clear compatibility message.
Useful? React with 👍 / 👎.
Cuts the (unmerged) Play Integrity Fix feature and adds LSPosed instead, plus corrects integrity copy that overclaimed.
Why cut PIF
On BlueStacks a green Play Integrity verdict isn't reachable — Google gates emulator integrity to
MEETS_VIRTUAL_INTEGRITY, which is exclusive to Google Play Games. We confirmed it empirically (root hidden via MagiskHide + fresh Pixel fingerprint spoofing in DroidGuard + fresh security patch + a clean GMS still returns no verdict), and no BlueStacks rooter — including the polished competitors — claims to pass it. Shipping a PIF button implied a capability the platform can't deliver.What's added
lsposed_payload.py: pinned, hash-locked LSPosed v1.9.2 (7024) zygisk variant, download-at-runtime, mirroring the ReZygisk payload.install_module), shown once the manager is installed, like ReZygisk. Needs ReZygisk (Zygisk) first.155 tests green.