s''
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
Dockerfile
|
||||||
|
houdini/
|
||||||
|
magisk/
|
||||||
|
mindthegapps/
|
||||||
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Copyright © 2023 Rikka
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
136
README.md
136
README.md
@@ -1,92 +1,102 @@
|
|||||||
# redroid-android13
|
# redroid-android13
|
||||||
|
|
||||||
|
这个仓库只保留了 `5570` 那套优化过的 Android 13 redroid 最小集,不再带 `gapps/litegapps/ndk/widevine` 这些无关入口。
|
||||||
|
|
||||||
|
目标很明确:
|
||||||
|
|
||||||
## Getting started
|
- 基于 `redroid/redroid:13.0.0`
|
||||||
|
- 集成 `MindTheGapps`
|
||||||
|
- 集成 `libhoudini`
|
||||||
|
- 集成 `Kitsune Mask / Magisk Delta`
|
||||||
|
- 启动后伪装成 `Pixel 7 Pro`
|
||||||
|
- 修复 `Magisk Installed N/A` 和设置页不可用的问题
|
||||||
|
|
||||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
## 保留的文件
|
||||||
|
|
||||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
- `redroid.py`
|
||||||
|
- `run_redroid.py`
|
||||||
|
- `bootstrap_redroid.py`
|
||||||
|
- `stuff/mindthegapps.py`
|
||||||
|
- `stuff/houdini.py`
|
||||||
|
- `stuff/houdini_hack.py`
|
||||||
|
- `stuff/magisk.py`
|
||||||
|
- `assets/redroid_extreme_post_fs_data.sh`
|
||||||
|
|
||||||
## Add your files
|
## 依赖
|
||||||
|
|
||||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
```bash
|
||||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
python -m pip install -r requirements.txt
|
||||||
|
|
||||||
```
|
|
||||||
cd existing_repo
|
|
||||||
git remote add origin http://gitlab.yidooplanet.com/gitlab-instance-0a899031/redroid-android13.git
|
|
||||||
git branch -M main
|
|
||||||
git push -uf origin main
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Integrate with your tools
|
容器侧默认按 `docker` 写,`podman` 也能用。
|
||||||
|
|
||||||
- [ ] [Set up project integrations](http://gitlab.yidooplanet.com/gitlab-instance-0a899031/redroid-android13/-/settings/integrations)
|
## 构建 5570 对应镜像
|
||||||
|
|
||||||
## Collaborate with your team
|
```bash
|
||||||
|
python redroid.py -mtg -i -m
|
||||||
|
```
|
||||||
|
|
||||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
这会生成并构建:
|
||||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
|
||||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
|
||||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
|
||||||
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
|
||||||
|
|
||||||
## Test and Deploy
|
```bash
|
||||||
|
redroid/redroid:13.0.0_mindthegapps_houdini_magisk
|
||||||
|
```
|
||||||
|
|
||||||
Use the built-in continuous integration in GitLab.
|
## 启动容器
|
||||||
|
|
||||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
```bash
|
||||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
python run_redroid.py \
|
||||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
--image redroid/redroid:13.0.0_mindthegapps_houdini_magisk \
|
||||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
--bridge libhoudini \
|
||||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
--device-profile pixel-7-pro \
|
||||||
|
--data-dir ~/data-redroid13 \
|
||||||
|
--port 5570 \
|
||||||
|
--replace
|
||||||
|
```
|
||||||
|
|
||||||
***
|
## 启动后做 5570 硬化
|
||||||
|
|
||||||
# Editing this README
|
```bash
|
||||||
|
python bootstrap_redroid.py \
|
||||||
|
--serial 127.0.0.1:5570 \
|
||||||
|
--device-profile pixel-7-pro \
|
||||||
|
--proxy-host auto \
|
||||||
|
--install-extreme-script
|
||||||
|
```
|
||||||
|
|
||||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
这一步会做 5 件事:
|
||||||
|
|
||||||
## Suggestions for a good README
|
- 跳过 SetupWizard
|
||||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
- 标记系统已完成初始化
|
||||||
|
- 开启高精度定位模式
|
||||||
|
- 允许 `com.android.shell` 使用 mock location
|
||||||
|
- 应用 `Pixel 7 Pro / user / release-keys / locked / green` 画像
|
||||||
|
|
||||||
## Name
|
如果带上 `--install-extreme-script`,还会把 [`assets/redroid_extreme_post_fs_data.sh`](C:\Users\admin\Desktop\redroid-android13\assets\redroid_extreme_post_fs_data.sh) 安装到 `/data/adb/post-fs-data.d/20-redroid-extreme.sh`,让下面两件事在重启后继续成立:
|
||||||
Choose a self-explaining name for your project.
|
|
||||||
|
|
||||||
## Description
|
- `vendor/bin/su` 指向 Magisk applet,应用命名空间可见
|
||||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
- `Pixel 7 Pro` 属性在每次开机时重新注入
|
||||||
|
|
||||||
## Badges
|
## 已验证结果
|
||||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
|
||||||
|
|
||||||
## Visuals
|
这套仓库对应的 `5570` 实例已经验证过:
|
||||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
|
||||||
|
|
||||||
## Installation
|
- `ro.product.model=Pixel 7 Pro`
|
||||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
- `ro.build.type=user`
|
||||||
|
- `ro.build.tags=release-keys`
|
||||||
|
- `ro.boot.vbmeta.device_state=locked`
|
||||||
|
- `ro.boot.verifiedbootstate=green`
|
||||||
|
- 应用 UID 下 `su -V` 返回 `27001`
|
||||||
|
- 应用 UID 下 `su -mm -c id` 能拿到 `uid=0(root)`
|
||||||
|
- `Kitsune` 首页的 `Installed` 正常显示
|
||||||
|
- `Kitsune` 设置页正常打开,`SuList / MagiskHide` 可配置
|
||||||
|
|
||||||
## Usage
|
## 当前范围
|
||||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
|
||||||
|
|
||||||
## Support
|
这个仓库只负责把 `5570` 的系统环境复现出来,不包含:
|
||||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
|
||||||
|
|
||||||
## Roadmap
|
- 虚拟摄像头
|
||||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
- camera HAL
|
||||||
|
- TikTok Live 专用补丁
|
||||||
|
|
||||||
## Contributing
|
后续如果要继续推进直播链路,直接在这个仓库往上叠加即可。
|
||||||
State if you are open to contributions and what your requirements are for accepting them.
|
|
||||||
|
|
||||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
|
||||||
|
|
||||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
|
||||||
|
|
||||||
## Authors and acknowledgment
|
|
||||||
Show your appreciation to those who have contributed to the project.
|
|
||||||
|
|
||||||
## License
|
|
||||||
For open source projects, say how it is licensed.
|
|
||||||
|
|
||||||
## Project status
|
|
||||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
|
||||||
|
|||||||
51
assets/redroid_extreme_post_fs_data.sh
Normal file
51
assets/redroid_extreme_post_fs_data.sh
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/system/bin/sh
|
||||||
|
|
||||||
|
MAGISKTMP=/debug_ramdisk
|
||||||
|
RESETPROP="$MAGISKTMP/resetprop"
|
||||||
|
|
||||||
|
if [ ! -x "$MAGISKTMP/magisk" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make Magisk's applets discoverable from app namespaces before zygote forks.
|
||||||
|
ln -sf "$MAGISKTMP/magisk" /vendor/bin/su
|
||||||
|
ln -sf "$MAGISKTMP/magisk" /vendor/bin/magisk
|
||||||
|
ln -sf "$MAGISKTMP/magisk" /vendor/bin/resetprop
|
||||||
|
ln -sf "$MAGISKTMP/magiskpolicy" /vendor/bin/magiskpolicy
|
||||||
|
|
||||||
|
if [ ! -x "$RESETPROP" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$RESETPROP" ro.product.model "Pixel 7 Pro"
|
||||||
|
"$RESETPROP" ro.product.brand google
|
||||||
|
"$RESETPROP" ro.product.name cheetah
|
||||||
|
"$RESETPROP" ro.product.device cheetah
|
||||||
|
"$RESETPROP" ro.product.manufacturer Google
|
||||||
|
"$RESETPROP" ro.product.system.model "Pixel 7 Pro"
|
||||||
|
"$RESETPROP" ro.product.system.brand google
|
||||||
|
"$RESETPROP" ro.product.system.name cheetah
|
||||||
|
"$RESETPROP" ro.product.system.device cheetah
|
||||||
|
"$RESETPROP" ro.product.system.manufacturer Google
|
||||||
|
"$RESETPROP" ro.product.vendor.model "Pixel 7 Pro"
|
||||||
|
"$RESETPROP" ro.product.vendor.brand google
|
||||||
|
"$RESETPROP" ro.product.vendor.name cheetah
|
||||||
|
"$RESETPROP" ro.product.vendor.device cheetah
|
||||||
|
"$RESETPROP" ro.product.vendor.manufacturer Google
|
||||||
|
"$RESETPROP" ro.product.product.model "Pixel 7 Pro"
|
||||||
|
"$RESETPROP" ro.product.product.brand google
|
||||||
|
"$RESETPROP" ro.product.product.name cheetah
|
||||||
|
"$RESETPROP" ro.product.product.device cheetah
|
||||||
|
"$RESETPROP" ro.product.product.manufacturer Google
|
||||||
|
"$RESETPROP" ro.build.fingerprint google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys
|
||||||
|
"$RESETPROP" ro.system.build.fingerprint google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys
|
||||||
|
"$RESETPROP" ro.vendor.build.fingerprint google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys
|
||||||
|
"$RESETPROP" ro.product.build.fingerprint google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys
|
||||||
|
"$RESETPROP" ro.build.type user
|
||||||
|
"$RESETPROP" ro.build.tags release-keys
|
||||||
|
"$RESETPROP" ro.secure 1
|
||||||
|
"$RESETPROP" ro.debuggable 0
|
||||||
|
"$RESETPROP" ro.boot.verifiedbootstate green
|
||||||
|
"$RESETPROP" ro.boot.vbmeta.device_state locked
|
||||||
|
"$RESETPROP" ro.boot.flash.locked 1
|
||||||
|
"$RESETPROP" ro.boot.veritymode enforcing
|
||||||
311
bootstrap_redroid.py
Normal file
311
bootstrap_redroid.py
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
from run_redroid import DEVICE_PROFILES
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd, check=True, capture_output=False):
|
||||||
|
return subprocess.run(
|
||||||
|
cmd,
|
||||||
|
check=check,
|
||||||
|
text=True,
|
||||||
|
capture_output=capture_output,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def adb(serial, args, check=True, capture_output=False):
|
||||||
|
return run(
|
||||||
|
["adb", "-s", serial] + args,
|
||||||
|
check=check,
|
||||||
|
capture_output=capture_output,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Backend:
|
||||||
|
def __init__(self, serial="", container_name="", container_runtime="docker"):
|
||||||
|
self.serial = serial
|
||||||
|
self.container_name = container_name
|
||||||
|
self.container_runtime = container_runtime
|
||||||
|
self._root_checked = False
|
||||||
|
self._root_available = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode(self):
|
||||||
|
return "container" if self.container_name else "adb"
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
if self.mode == "adb" and ":" in self.serial:
|
||||||
|
run(["adb", "connect", self.serial], check=False)
|
||||||
|
|
||||||
|
def has_root(self):
|
||||||
|
if self.mode == "container":
|
||||||
|
return True
|
||||||
|
if not self._root_checked:
|
||||||
|
result = adb(
|
||||||
|
self.serial,
|
||||||
|
["shell", "su", "-mm", "-c", "id"],
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
self._root_available = result.returncode == 0 and "uid=0(" in result.stdout
|
||||||
|
self._root_checked = True
|
||||||
|
return self._root_available
|
||||||
|
|
||||||
|
def exec_shell(self, command, check=True, capture_output=False, root=False):
|
||||||
|
if self.mode == "container":
|
||||||
|
return run(
|
||||||
|
[
|
||||||
|
self.container_runtime,
|
||||||
|
"exec",
|
||||||
|
self.container_name,
|
||||||
|
"/system/bin/sh",
|
||||||
|
"-c",
|
||||||
|
command,
|
||||||
|
],
|
||||||
|
check=check,
|
||||||
|
capture_output=capture_output,
|
||||||
|
)
|
||||||
|
if root and self.has_root():
|
||||||
|
return adb(
|
||||||
|
self.serial,
|
||||||
|
["shell", "su", "-mm", "-c", command],
|
||||||
|
check=check,
|
||||||
|
capture_output=capture_output,
|
||||||
|
)
|
||||||
|
return adb(
|
||||||
|
self.serial,
|
||||||
|
["shell", command],
|
||||||
|
check=check,
|
||||||
|
capture_output=capture_output,
|
||||||
|
)
|
||||||
|
|
||||||
|
def wait_for_boot(self, timeout):
|
||||||
|
deadline = time.time() + timeout
|
||||||
|
if self.mode == "adb":
|
||||||
|
run(["adb", "-s", self.serial, "wait-for-device"])
|
||||||
|
while time.time() < deadline:
|
||||||
|
boot = self.exec_shell(
|
||||||
|
"getprop sys.boot_completed",
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
).stdout.strip()
|
||||||
|
if boot == "1":
|
||||||
|
return
|
||||||
|
time.sleep(2)
|
||||||
|
raise TimeoutError("Timed out waiting for Android boot completion")
|
||||||
|
|
||||||
|
|
||||||
|
def try_shell(backend, command, root=False):
|
||||||
|
backend.exec_shell(command, check=False, root=root)
|
||||||
|
|
||||||
|
|
||||||
|
def put_setting(backend, namespace, key, value):
|
||||||
|
try_shell(backend, "settings put {} {} {}".format(namespace, key, value))
|
||||||
|
|
||||||
|
|
||||||
|
def delete_setting(backend, namespace, key):
|
||||||
|
try_shell(backend, "settings delete {} {}".format(namespace, key))
|
||||||
|
|
||||||
|
|
||||||
|
def detect_gateway_ip():
|
||||||
|
system = platform.system().lower()
|
||||||
|
if system == "linux":
|
||||||
|
result = run(
|
||||||
|
["sh", "-lc", "ip route show default 2>/dev/null | awk 'NR==1 {print $3}'"],
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
gateway = result.stdout.strip()
|
||||||
|
if gateway:
|
||||||
|
return gateway
|
||||||
|
elif system == "windows":
|
||||||
|
result = run(
|
||||||
|
[
|
||||||
|
"powershell",
|
||||||
|
"-NoProfile",
|
||||||
|
"-Command",
|
||||||
|
"(Get-NetRoute -DestinationPrefix '0.0.0.0/0' | "
|
||||||
|
"Sort-Object RouteMetric | Select-Object -First 1).NextHop",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
)
|
||||||
|
gateway = result.stdout.strip()
|
||||||
|
if gateway:
|
||||||
|
return gateway
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def configure_proxy(backend, proxy_host, proxy_port):
|
||||||
|
if proxy_host == "none":
|
||||||
|
delete_setting(backend, "global", "http_proxy")
|
||||||
|
return "disabled"
|
||||||
|
host = proxy_host
|
||||||
|
if proxy_host == "auto":
|
||||||
|
host = detect_gateway_ip()
|
||||||
|
if not host:
|
||||||
|
return "unresolved"
|
||||||
|
put_setting(backend, "global", "http_proxy", "{}:{}".format(host, proxy_port))
|
||||||
|
return "{}:{}".format(host, proxy_port)
|
||||||
|
|
||||||
|
|
||||||
|
def shell_quote(value):
|
||||||
|
return shlex.quote(value)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_device_profile(backend, profile):
|
||||||
|
if profile == "none":
|
||||||
|
return "skipped"
|
||||||
|
props = DEVICE_PROFILES[profile]
|
||||||
|
resetprop = backend.exec_shell(
|
||||||
|
"if [ -x /debug_ramdisk/resetprop ]; then echo /debug_ramdisk/resetprop; "
|
||||||
|
"elif command -v resetprop >/dev/null 2>&1; then command -v resetprop; fi",
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
).stdout.strip()
|
||||||
|
tool = resetprop or "setprop"
|
||||||
|
for item in props:
|
||||||
|
key, value = item.split("=", 1)
|
||||||
|
try_shell(
|
||||||
|
backend,
|
||||||
|
"{} {} {}".format(
|
||||||
|
tool,
|
||||||
|
shell_quote(key),
|
||||||
|
shell_quote(value),
|
||||||
|
),
|
||||||
|
root=True,
|
||||||
|
)
|
||||||
|
return "{} via {}".format(profile, tool)
|
||||||
|
|
||||||
|
|
||||||
|
def asset_path(*parts):
|
||||||
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), *parts)
|
||||||
|
|
||||||
|
|
||||||
|
def install_extreme_script(backend):
|
||||||
|
if backend.mode != "container" and not backend.has_root():
|
||||||
|
return "root unavailable"
|
||||||
|
|
||||||
|
local_script = asset_path("assets", "redroid_extreme_post_fs_data.sh")
|
||||||
|
if not os.path.isfile(local_script):
|
||||||
|
return "missing asset"
|
||||||
|
|
||||||
|
tmp_path = "/data/local/tmp/20-redroid-extreme.sh"
|
||||||
|
final_path = "/data/adb/post-fs-data.d/20-redroid-extreme.sh"
|
||||||
|
|
||||||
|
if backend.mode == "container":
|
||||||
|
run(
|
||||||
|
[
|
||||||
|
backend.container_runtime,
|
||||||
|
"cp",
|
||||||
|
local_script,
|
||||||
|
"{}:{}".format(backend.container_name, tmp_path),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
run(["adb", "-s", backend.serial, "push", local_script, tmp_path])
|
||||||
|
|
||||||
|
try_shell(
|
||||||
|
backend,
|
||||||
|
"mkdir -p /data/adb/post-fs-data.d && "
|
||||||
|
"cp {} {} && "
|
||||||
|
"chown 0:0 {} && "
|
||||||
|
"chmod 0755 {} && "
|
||||||
|
"rm -f {}".format(tmp_path, final_path, final_path, final_path, tmp_path),
|
||||||
|
root=True,
|
||||||
|
)
|
||||||
|
return final_path
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Apply post-boot hardening to a redroid instance."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--serial",
|
||||||
|
default="127.0.0.1:5555",
|
||||||
|
help="ADB serial, for example 127.0.0.1:5555",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--container-name",
|
||||||
|
help="Operate on a running redroid container directly instead of using ADB",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--container-runtime",
|
||||||
|
default="docker",
|
||||||
|
choices=["docker", "podman"],
|
||||||
|
help="Container runtime when --container-name is used",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--timeout",
|
||||||
|
type=int,
|
||||||
|
default=300,
|
||||||
|
help="Seconds to wait for Android to finish booting",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--proxy-host",
|
||||||
|
default="auto",
|
||||||
|
help="Proxy host, auto, or none",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--proxy-port",
|
||||||
|
type=int,
|
||||||
|
default=20171,
|
||||||
|
help="Proxy port when proxy is enabled",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--device-profile",
|
||||||
|
choices=["none"] + sorted(DEVICE_PROFILES.keys()),
|
||||||
|
default="none",
|
||||||
|
help="Apply a runtime device profile through resetprop when available",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--install-extreme-script",
|
||||||
|
action="store_true",
|
||||||
|
help="Install the validated 5570 post-fs-data script into /data/adb/post-fs-data.d",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
backend = Backend(
|
||||||
|
serial=args.serial,
|
||||||
|
container_name=args.container_name or "",
|
||||||
|
container_runtime=args.container_runtime,
|
||||||
|
)
|
||||||
|
backend.connect()
|
||||||
|
backend.wait_for_boot(args.timeout)
|
||||||
|
|
||||||
|
put_setting(backend, "global", "device_provisioned", "1")
|
||||||
|
put_setting(backend, "secure", "user_setup_complete", "1")
|
||||||
|
put_setting(backend, "secure", "location_mode", "3")
|
||||||
|
put_setting(backend, "global", "package_verifier_enable", "0")
|
||||||
|
put_setting(backend, "global", "verifier_verify_adb_installs", "0")
|
||||||
|
|
||||||
|
try_shell(backend, "pm disable-user --user 0 com.android.setupwizard")
|
||||||
|
try_shell(backend, "pm disable-user --user 0 com.google.android.setupwizard")
|
||||||
|
try_shell(backend, "appops set com.android.shell android:mock_location allow")
|
||||||
|
|
||||||
|
proxy_state = configure_proxy(backend, args.proxy_host, args.proxy_port)
|
||||||
|
profile_state = apply_device_profile(backend, args.device_profile)
|
||||||
|
extreme_script_state = "skipped"
|
||||||
|
if args.install_extreme_script:
|
||||||
|
extreme_script_state = install_extreme_script(backend)
|
||||||
|
|
||||||
|
if backend.mode == "container":
|
||||||
|
print("Container: {}".format(backend.container_name))
|
||||||
|
else:
|
||||||
|
print("ADB serial: {}".format(backend.serial))
|
||||||
|
print("Boot status: completed")
|
||||||
|
print("SetupWizard: disabled")
|
||||||
|
print("Provisioned: yes")
|
||||||
|
print("Mock location for shell: allowed")
|
||||||
|
print("HTTP proxy: {}".format(proxy_state))
|
||||||
|
print("Device profile: {}".format(profile_state))
|
||||||
|
print("Extreme script: {}".format(extreme_script_state))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
124
redroid.py
Normal file
124
redroid.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from stuff.houdini import Houdini
|
||||||
|
from stuff.houdini_hack import Houdini_Hack
|
||||||
|
from stuff.magisk import Magisk
|
||||||
|
from stuff.mindthegapps import MindTheGapps
|
||||||
|
import tools.helper as helper
|
||||||
|
|
||||||
|
|
||||||
|
SUPPORTED_ANDROID_VERSIONS = ["13.0.0"]
|
||||||
|
|
||||||
|
|
||||||
|
def build_layers(args):
|
||||||
|
dockerfile = "FROM redroid/redroid:{}-latest\n".format(args.android)
|
||||||
|
tags = [args.android]
|
||||||
|
|
||||||
|
if args.mindthegapps:
|
||||||
|
MindTheGapps(args.android).install()
|
||||||
|
dockerfile += "COPY mindthegapps /\n"
|
||||||
|
tags.append("mindthegapps")
|
||||||
|
|
||||||
|
if args.houdini:
|
||||||
|
arch = helper.host()[0]
|
||||||
|
if arch not in {"x86", "x86_64"}:
|
||||||
|
raise ValueError("Houdini only makes sense on x86/x86_64 hosts")
|
||||||
|
Houdini(args.android).install()
|
||||||
|
Houdini_Hack(args.android).install()
|
||||||
|
dockerfile += "COPY houdini /\n"
|
||||||
|
tags.append("houdini")
|
||||||
|
|
||||||
|
if args.magisk:
|
||||||
|
Magisk().install()
|
||||||
|
dockerfile += "COPY magisk /\n"
|
||||||
|
tags.append("magisk")
|
||||||
|
|
||||||
|
return dockerfile, tags
|
||||||
|
|
||||||
|
|
||||||
|
def print_recommended_flow(image_name):
|
||||||
|
print("\nRecommended 5570 launch")
|
||||||
|
print(
|
||||||
|
"python run_redroid.py "
|
||||||
|
"--image {} "
|
||||||
|
"--bridge libhoudini "
|
||||||
|
"--device-profile pixel-7-pro "
|
||||||
|
"--data-dir ~/data-redroid13 "
|
||||||
|
"--port 5570 "
|
||||||
|
"--replace\n".format(image_name)
|
||||||
|
)
|
||||||
|
print("Recommended post-boot hardening")
|
||||||
|
print(
|
||||||
|
"python bootstrap_redroid.py "
|
||||||
|
"--serial 127.0.0.1:5570 "
|
||||||
|
"--device-profile pixel-7-pro "
|
||||||
|
"--proxy-host auto "
|
||||||
|
"--install-extreme-script\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Build the minimal Android 13 redroid image used for the optimized 5570 setup."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-a",
|
||||||
|
"--android-version",
|
||||||
|
dest="android",
|
||||||
|
default="13.0.0",
|
||||||
|
choices=SUPPORTED_ANDROID_VERSIONS,
|
||||||
|
help="Android base image to build from",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-mtg",
|
||||||
|
"--install-mindthegapps",
|
||||||
|
dest="mindthegapps",
|
||||||
|
action="store_true",
|
||||||
|
help="Install MindTheGapps into the image",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-i",
|
||||||
|
"--install-houdini",
|
||||||
|
dest="houdini",
|
||||||
|
action="store_true",
|
||||||
|
help="Install libhoudini and the Android 13 compatibility hack",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-m",
|
||||||
|
"--install-magisk",
|
||||||
|
dest="magisk",
|
||||||
|
action="store_true",
|
||||||
|
help="Install Kitsune Mask / Magisk Delta bootless payload",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--container",
|
||||||
|
dest="container",
|
||||||
|
default="docker",
|
||||||
|
choices=["docker", "podman"],
|
||||||
|
help="Container runtime",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
dockerfile, tags = build_layers(args)
|
||||||
|
|
||||||
|
print("\nDockerfile\n{}".format(dockerfile))
|
||||||
|
with open("./Dockerfile", "w", encoding="utf-8") as handle:
|
||||||
|
handle.write(dockerfile)
|
||||||
|
|
||||||
|
image_name = "redroid/redroid:" + "_".join(tags)
|
||||||
|
subprocess.run([args.container, "build", "-t", image_name, "."], check=True)
|
||||||
|
helper.print_color(
|
||||||
|
"Successfully built {}".format(image_name),
|
||||||
|
helper.bcolors.GREEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.houdini:
|
||||||
|
print_recommended_flow(image_name)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
requests==2.28.1
|
||||||
|
tqdm==4.64.1
|
||||||
324
run_redroid.py
Normal file
324
run_redroid.py
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from tools.helper import bcolors, print_color
|
||||||
|
|
||||||
|
|
||||||
|
COMMON_BRIDGE_PROPS = [
|
||||||
|
"ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi",
|
||||||
|
"ro.product.cpu.abilist64=x86_64,arm64-v8a",
|
||||||
|
"ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi",
|
||||||
|
"ro.dalvik.vm.isa.arm=x86",
|
||||||
|
"ro.dalvik.vm.isa.arm64=x86_64",
|
||||||
|
"ro.enable.native.bridge.exec=1",
|
||||||
|
"ro.enable.native.bridge.exec64=1",
|
||||||
|
"ro.vendor.enable.native.bridge.exec=1",
|
||||||
|
"ro.vendor.enable.native.bridge.exec64=1",
|
||||||
|
]
|
||||||
|
|
||||||
|
DEVICE_PROFILES = {
|
||||||
|
"pixel-3-xl": [
|
||||||
|
"ro.product.model=Pixel 3 XL",
|
||||||
|
"ro.product.brand=google",
|
||||||
|
"ro.product.name=crosshatch",
|
||||||
|
"ro.product.device=crosshatch",
|
||||||
|
"ro.product.manufacturer=Google",
|
||||||
|
"ro.product.system.model=Pixel 3 XL",
|
||||||
|
"ro.product.system.brand=google",
|
||||||
|
"ro.product.system.name=crosshatch",
|
||||||
|
"ro.product.system.device=crosshatch",
|
||||||
|
"ro.product.system.manufacturer=Google",
|
||||||
|
"ro.product.vendor.model=Pixel 3 XL",
|
||||||
|
"ro.product.vendor.brand=google",
|
||||||
|
"ro.product.vendor.name=crosshatch",
|
||||||
|
"ro.product.vendor.device=crosshatch",
|
||||||
|
"ro.product.vendor.manufacturer=Google",
|
||||||
|
"ro.product.product.model=Pixel 3 XL",
|
||||||
|
"ro.product.product.brand=google",
|
||||||
|
"ro.product.product.name=crosshatch",
|
||||||
|
"ro.product.product.device=crosshatch",
|
||||||
|
"ro.product.product.manufacturer=Google",
|
||||||
|
"ro.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
|
||||||
|
"ro.system.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
|
||||||
|
"ro.vendor.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
|
||||||
|
"ro.product.build.fingerprint=google/crosshatch/crosshatch:11/RQ3A.211001.001/7641976:user/release-keys",
|
||||||
|
"ro.build.type=user",
|
||||||
|
"ro.build.tags=release-keys",
|
||||||
|
"ro.secure=1",
|
||||||
|
"ro.debuggable=0",
|
||||||
|
"ro.boot.verifiedbootstate=green",
|
||||||
|
"ro.boot.vbmeta.device_state=locked",
|
||||||
|
"ro.boot.flash.locked=1",
|
||||||
|
"ro.boot.veritymode=enforcing",
|
||||||
|
],
|
||||||
|
"pixel-7-pro": [
|
||||||
|
"ro.product.model=Pixel 7 Pro",
|
||||||
|
"ro.product.brand=google",
|
||||||
|
"ro.product.name=cheetah",
|
||||||
|
"ro.product.device=cheetah",
|
||||||
|
"ro.product.manufacturer=Google",
|
||||||
|
"ro.product.system.model=Pixel 7 Pro",
|
||||||
|
"ro.product.system.brand=google",
|
||||||
|
"ro.product.system.name=cheetah",
|
||||||
|
"ro.product.system.device=cheetah",
|
||||||
|
"ro.product.system.manufacturer=Google",
|
||||||
|
"ro.product.vendor.model=Pixel 7 Pro",
|
||||||
|
"ro.product.vendor.brand=google",
|
||||||
|
"ro.product.vendor.name=cheetah",
|
||||||
|
"ro.product.vendor.device=cheetah",
|
||||||
|
"ro.product.vendor.manufacturer=Google",
|
||||||
|
"ro.product.product.model=Pixel 7 Pro",
|
||||||
|
"ro.product.product.brand=google",
|
||||||
|
"ro.product.product.name=cheetah",
|
||||||
|
"ro.product.product.device=cheetah",
|
||||||
|
"ro.product.product.manufacturer=Google",
|
||||||
|
"ro.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
|
||||||
|
"ro.system.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
|
||||||
|
"ro.vendor.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
|
||||||
|
"ro.product.build.fingerprint=google/cheetah/cheetah:13/TQ3A.230805.001/2023080800:user/release-keys",
|
||||||
|
"ro.build.type=user",
|
||||||
|
"ro.build.tags=release-keys",
|
||||||
|
"ro.secure=1",
|
||||||
|
"ro.debuggable=0",
|
||||||
|
"ro.boot.verifiedbootstate=green",
|
||||||
|
"ro.boot.vbmeta.device_state=locked",
|
||||||
|
"ro.boot.flash.locked=1",
|
||||||
|
"ro.boot.veritymode=enforcing",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
LAUNCH_PRESETS = {
|
||||||
|
"redroid-pro": {
|
||||||
|
"props": [
|
||||||
|
"androidboot.redroid_width=720",
|
||||||
|
"androidboot.redroid_height=1280",
|
||||||
|
"androidboot.redroid_dpi=320",
|
||||||
|
"androidboot.redroid_fps=30",
|
||||||
|
"androidboot.redroid_vm_heap=256m",
|
||||||
|
"ro.config.low_ram=true",
|
||||||
|
"config.disable_consumerir=true",
|
||||||
|
],
|
||||||
|
"device_profile": "pixel-3-xl",
|
||||||
|
"memory": "4g",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def infer_bridge(image, requested):
|
||||||
|
if requested != "auto":
|
||||||
|
return requested
|
||||||
|
if "houdini" in image:
|
||||||
|
return "libhoudini"
|
||||||
|
if "ndk" in image:
|
||||||
|
return "libndk"
|
||||||
|
return "none"
|
||||||
|
|
||||||
|
|
||||||
|
def bridge_props(mode):
|
||||||
|
if mode == "none":
|
||||||
|
return []
|
||||||
|
props = list(COMMON_BRIDGE_PROPS)
|
||||||
|
if mode == "libhoudini":
|
||||||
|
props.append("ro.dalvik.vm.native.bridge=libhoudini.so")
|
||||||
|
elif mode == "libnb":
|
||||||
|
props.append("ro.dalvik.vm.native.bridge=libnb.so")
|
||||||
|
elif mode == "libndk":
|
||||||
|
props.append("ro.dalvik.vm.native.bridge=libndk_translation.so")
|
||||||
|
props.append("ro.ndk_translation.version=0.2.3")
|
||||||
|
else:
|
||||||
|
raise ValueError("Unsupported bridge mode: {}".format(mode))
|
||||||
|
return props
|
||||||
|
|
||||||
|
|
||||||
|
def device_profile_props(profile):
|
||||||
|
if profile == "none":
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
return list(DEVICE_PROFILES[profile])
|
||||||
|
except KeyError as exc:
|
||||||
|
raise ValueError("Unsupported device profile: {}".format(profile)) from exc
|
||||||
|
|
||||||
|
|
||||||
|
def preset_props(preset):
|
||||||
|
if preset == "none":
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
return list(LAUNCH_PRESETS[preset]["props"])
|
||||||
|
except KeyError as exc:
|
||||||
|
raise ValueError("Unsupported preset: {}".format(preset)) from exc
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_device_profile(preset, requested):
|
||||||
|
if requested != "none":
|
||||||
|
return requested
|
||||||
|
if preset == "none":
|
||||||
|
return "none"
|
||||||
|
return LAUNCH_PRESETS[preset].get("device_profile", "none")
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_memory(preset, requested):
|
||||||
|
if requested:
|
||||||
|
return requested
|
||||||
|
if preset == "none":
|
||||||
|
return None
|
||||||
|
return LAUNCH_PRESETS[preset].get("memory")
|
||||||
|
|
||||||
|
|
||||||
|
def default_name(image):
|
||||||
|
name = image.split("/")[-1].replace(":", "_")
|
||||||
|
name = re.sub(r"[^a-zA-Z0-9_.-]+", "-", name).strip("-")
|
||||||
|
return name or "redroid"
|
||||||
|
|
||||||
|
|
||||||
|
def quote_cmd(args):
|
||||||
|
return " ".join(shlex.quote(part) for part in args)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Launch a redroid container with a tested native bridge configuration."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--image",
|
||||||
|
required=True,
|
||||||
|
help="Image name, for example redroid/redroid:13.0.0_mindthegapps_houdini_magisk",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--container",
|
||||||
|
default="docker",
|
||||||
|
choices=["docker", "podman"],
|
||||||
|
help="Container runtime",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--name",
|
||||||
|
help="Container name. Defaults to a slug derived from the image tag.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--data-dir",
|
||||||
|
default="~/data-redroid",
|
||||||
|
help="Host directory mapped to /data",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--port",
|
||||||
|
type=int,
|
||||||
|
default=5555,
|
||||||
|
help="Host TCP port mapped to guest ADB 5555",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--bridge",
|
||||||
|
choices=["auto", "none", "libhoudini", "libnb", "libndk"],
|
||||||
|
default="auto",
|
||||||
|
help="Native bridge runtime property to inject",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--preset",
|
||||||
|
choices=["none"] + sorted(LAUNCH_PRESETS.keys()),
|
||||||
|
default="none",
|
||||||
|
help="Inject a tested launch preset",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--gpu-mode",
|
||||||
|
default="guest",
|
||||||
|
help="Value for androidboot.redroid_gpu_mode",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--device-profile",
|
||||||
|
choices=["none"] + sorted(DEVICE_PROFILES.keys()),
|
||||||
|
default="none",
|
||||||
|
help="Inject a tested device identity profile",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--memory",
|
||||||
|
help="Optional container memory limit, for example 4g",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--replace",
|
||||||
|
action="store_true",
|
||||||
|
help="Remove an existing container with the same name before running",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--keep-container",
|
||||||
|
action="store_true",
|
||||||
|
help="Do not pass --rm to the runtime",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dry-run",
|
||||||
|
action="store_true",
|
||||||
|
help="Print the generated command without executing it",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--extra-prop",
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
help="Additional redroid property, repeatable",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
image = args.image
|
||||||
|
name = args.name or default_name(image)
|
||||||
|
data_dir = os.path.abspath(os.path.expanduser(args.data_dir))
|
||||||
|
bridge = infer_bridge(image, args.bridge)
|
||||||
|
preset = args.preset
|
||||||
|
device_profile = resolve_device_profile(preset, args.device_profile)
|
||||||
|
memory = resolve_memory(preset, args.memory)
|
||||||
|
|
||||||
|
os.makedirs(data_dir, exist_ok=True)
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
args.container,
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--privileged",
|
||||||
|
"--name",
|
||||||
|
name,
|
||||||
|
"-v",
|
||||||
|
"{}:/data".format(data_dir),
|
||||||
|
"-p",
|
||||||
|
"{}:5555".format(args.port),
|
||||||
|
]
|
||||||
|
if memory:
|
||||||
|
cmd.extend(["--memory", memory])
|
||||||
|
if not args.keep_container:
|
||||||
|
cmd.append("--rm")
|
||||||
|
cmd.extend(
|
||||||
|
[
|
||||||
|
image,
|
||||||
|
"androidboot.redroid_gpu_mode={}".format(args.gpu_mode),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
cmd.extend(bridge_props(bridge))
|
||||||
|
cmd.extend(preset_props(preset))
|
||||||
|
cmd.extend(device_profile_props(device_profile))
|
||||||
|
cmd.extend(args.extra_prop)
|
||||||
|
|
||||||
|
if args.replace and not args.dry_run:
|
||||||
|
subprocess.run(
|
||||||
|
[args.container, "rm", "-f", name],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
print_color("Selected native bridge: {}".format(bridge), bcolors.GREEN)
|
||||||
|
if preset != "none":
|
||||||
|
print_color("Selected launch preset: {}".format(preset), bcolors.GREEN)
|
||||||
|
if device_profile != "none":
|
||||||
|
print_color("Selected device profile: {}".format(device_profile), bcolors.GREEN)
|
||||||
|
print("Command:\n{}\n".format(quote_cmd(cmd)))
|
||||||
|
|
||||||
|
if args.dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
subprocess.run(cmd, check=True)
|
||||||
|
print_color("Container started as {}".format(name), bcolors.GREEN)
|
||||||
|
print("ADB: adb connect 127.0.0.1:{}".format(args.port))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
34
stuff/general.py
Normal file
34
stuff/general.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from tools.helper import bcolors, download_file, print_color
|
||||||
|
|
||||||
|
class General:
|
||||||
|
def download(self):
|
||||||
|
loc_md5 = ""
|
||||||
|
if os.path.isfile(self.dl_file_name):
|
||||||
|
with open(self.dl_file_name,"rb") as f:
|
||||||
|
bytes = f.read()
|
||||||
|
loc_md5 = hashlib.md5(bytes).hexdigest()
|
||||||
|
while not os.path.isfile(self.dl_file_name) or loc_md5 != self.act_md5:
|
||||||
|
if os.path.isfile(self.dl_file_name):
|
||||||
|
os.remove(self.dl_file_name)
|
||||||
|
print_color("md5 mismatches, redownloading now ....",bcolors.YELLOW)
|
||||||
|
loc_md5 = download_file(self.dl_link, self.dl_file_name)
|
||||||
|
|
||||||
|
def extract(self):
|
||||||
|
print_color("Extracting archive...", bcolors.GREEN)
|
||||||
|
print(self.dl_file_name)
|
||||||
|
print(self.extract_to)
|
||||||
|
with zipfile.ZipFile(self.dl_file_name) as z:
|
||||||
|
z.extractall(self.extract_to)
|
||||||
|
def copy(self):
|
||||||
|
pass
|
||||||
|
def install(self):
|
||||||
|
# pass
|
||||||
|
self.download()
|
||||||
|
self.extract()
|
||||||
|
self.copy()
|
||||||
96
stuff/houdini.py
Normal file
96
stuff/houdini.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
from stuff.general import General
|
||||||
|
from tools.helper import bcolors, get_download_dir, print_color, run
|
||||||
|
|
||||||
|
|
||||||
|
class Houdini(General):
|
||||||
|
download_loc = get_download_dir()
|
||||||
|
copy_dir = "./houdini"
|
||||||
|
init_rc_component = """
|
||||||
|
on early-init
|
||||||
|
mount binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
|
||||||
|
|
||||||
|
on property:ro.enable.native.bridge.exec=1
|
||||||
|
copy /vendor/etc/binfmt_misc/arm_exe /proc/sys/fs/binfmt_misc/register
|
||||||
|
copy /vendor/etc/binfmt_misc/arm_dyn /proc/sys/fs/binfmt_misc/register
|
||||||
|
|
||||||
|
on property:ro.enable.native.bridge.exec64=1
|
||||||
|
copy /vendor/etc/binfmt_misc/arm64_exe /proc/sys/fs/binfmt_misc/register
|
||||||
|
copy /vendor/etc/binfmt_misc/arm64_dyn /proc/sys/fs/binfmt_misc/register
|
||||||
|
|
||||||
|
on property:sys.boot_completed=1
|
||||||
|
exec -- /system/bin/sh -c "echo ':arm_exe:M::\\\\x7f\\\\x45\\\\x4c\\\\x46\\\\x01\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x02\\\\x00\\\\x28::/system/bin/houdini:P' >> /proc/sys/fs/binfmt_misc/register"
|
||||||
|
exec -- /system/bin/sh -c "echo ':arm_dyn:M::\\\\x7f\\\\x45\\\\x4c\\\\x46\\\\x01\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x03\\\\x00\\\\x28::/system/bin/houdini:P' >> /proc/sys/fs/binfmt_misc/register"
|
||||||
|
exec -- /system/bin/sh -c "echo ':arm64_exe:M::\\\\x7f\\\\x45\\\\x4c\\\\x46\\\\x02\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x02\\\\x00\\\\xb7::/system/bin/houdini64:P' >> /proc/sys/fs/binfmt_misc/register"
|
||||||
|
exec -- /system/bin/sh -c "echo ':arm64_dyn:M::\\\\x7f\\\\x45\\\\x4c\\\\x46\\\\x02\\\\x01\\\\x01\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x03\\\\x00\\\\xb7::/system/bin/houdini64:P' >> /proc/sys/fs/binfmt_misc/register"
|
||||||
|
"""
|
||||||
|
dl_links = {
|
||||||
|
# 8.1.0 from cros R79 Android N not working
|
||||||
|
# "8.1.0":[
|
||||||
|
# "https://github.com/rote66/vendor_intel_proprietary_houdini/archive/9246014ddf22d5e34f294d726dc48ca446b0e20e.zip",
|
||||||
|
# "b0a9ddc0d817d290a15b132091a37f36"],
|
||||||
|
"8.1.0":[
|
||||||
|
"https://github.com/rote66/vendor_intel_proprietary_houdini/archive/46682f423b8497db3f96222f2669d770eff764c3.zip",
|
||||||
|
"cd4dd2891aa18e7699d33dcc3fe3ffd4"],
|
||||||
|
"9.0.0":[
|
||||||
|
"https://github.com/rote66/vendor_intel_proprietary_houdini/archive/46682f423b8497db3f96222f2669d770eff764c3.zip",
|
||||||
|
"cd4dd2891aa18e7699d33dcc3fe3ffd4"],
|
||||||
|
"11.0.0": [
|
||||||
|
"https://github.com/rote66/vendor_intel_proprietary_houdini/archive/debc3dc91cf12b5c5b8a1c546a5b0b7bf7f838a8.zip",
|
||||||
|
"cb7ffac26d47ec7c89df43818e126b47"],
|
||||||
|
"12.0.0": [
|
||||||
|
"https://github.com/rote66/vendor_intel_proprietary_houdini/archive/debc3dc91cf12b5c5b8a1c546a5b0b7bf7f838a8.zip",
|
||||||
|
"cb7ffac26d47ec7c89df43818e126b47"],
|
||||||
|
"13.0.0": [
|
||||||
|
"https://github.com/rote66/vendor_intel_proprietary_houdini/archive/debc3dc91cf12b5c5b8a1c546a5b0b7bf7f838a8.zip",
|
||||||
|
"cb7ffac26d47ec7c89df43818e126b47"],
|
||||||
|
"14.0.0": [
|
||||||
|
"https://github.com/rote66/vendor_intel_proprietary_houdini/archive/debc3dc91cf12b5c5b8a1c546a5b0b7bf7f838a8.zip",
|
||||||
|
"cb7ffac26d47ec7c89df43818e126b47"]
|
||||||
|
# "15.0.0": [
|
||||||
|
# "https://github.com/rote66/vendor_intel_proprietary_houdini/archive/debc3dc91cf12b5c5b8a1c546a5b0b7bf7f838a8.zip",
|
||||||
|
# "cb7ffac26d47ec7c89df43818e126b47"]
|
||||||
|
}
|
||||||
|
dl_file_name = os.path.join(download_loc, "libhoudini.zip")
|
||||||
|
extract_to = os.path.join(download_loc, "houdini_extract")
|
||||||
|
|
||||||
|
def __init__(self, version):
|
||||||
|
self.version = version
|
||||||
|
if version in self.dl_links.keys():
|
||||||
|
self.dl_link = self.dl_links[version][0]
|
||||||
|
self.act_md5 = self.dl_links[version][1]
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
"No available libhoudini for Android {}".format(version))
|
||||||
|
|
||||||
|
def download(self):
|
||||||
|
print_color("Downloading libhoudini now .....", bcolors.GREEN)
|
||||||
|
super().download()
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
if os.path.exists(self.copy_dir):
|
||||||
|
shutil.rmtree(self.copy_dir)
|
||||||
|
run(["chmod", "+x", self.extract_to, "-R"])
|
||||||
|
|
||||||
|
print_color("Copying libhoudini library files ...", bcolors.GREEN)
|
||||||
|
name = re.findall(r"([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||||
|
shutil.copytree(os.path.join(self.extract_to, "vendor_intel_proprietary_houdini-" + name,
|
||||||
|
"prebuilts"), os.path.join(self.copy_dir, "system"), dirs_exist_ok=True)
|
||||||
|
|
||||||
|
system_binfmt_dir = os.path.join(self.copy_dir, "system", "etc", "binfmt_misc")
|
||||||
|
vendor_binfmt_dir = os.path.join(self.copy_dir, "vendor", "etc", "binfmt_misc")
|
||||||
|
os.makedirs(vendor_binfmt_dir, exist_ok=True)
|
||||||
|
for filename in ["arm_exe", "arm_dyn", "arm64_exe", "arm64_dyn"]:
|
||||||
|
shutil.copyfile(
|
||||||
|
os.path.join(system_binfmt_dir, filename),
|
||||||
|
os.path.join(vendor_binfmt_dir, filename),
|
||||||
|
)
|
||||||
|
|
||||||
|
init_path = os.path.join(self.copy_dir, "vendor", "etc", "init", "houdini.rc")
|
||||||
|
if not os.path.isfile(init_path):
|
||||||
|
os.makedirs(os.path.dirname(init_path), exist_ok=True)
|
||||||
|
with open(init_path, "w") as initfile:
|
||||||
|
initfile.write(self.init_rc_component)
|
||||||
|
os.chmod(init_path, 0o644)
|
||||||
33
stuff/houdini_hack.py
Normal file
33
stuff/houdini_hack.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
from stuff.general import General
|
||||||
|
from tools.helper import bcolors, get_download_dir, print_color, run
|
||||||
|
|
||||||
|
|
||||||
|
class Houdini_Hack(General):
|
||||||
|
download_loc = get_download_dir()
|
||||||
|
copy_dir = "./houdini"
|
||||||
|
dl_file_name = os.path.join(download_loc, "libhoudini_hack.zip")
|
||||||
|
extract_to = os.path.join(download_loc, "houdini_hack_extract")
|
||||||
|
|
||||||
|
def __init__(self, version):
|
||||||
|
self.version = version
|
||||||
|
self.dl_link = "https://github.com/rote66/redroid_libhoudini_hack/archive/a2194c5e294cbbfdfe87e51eb9eddb4c3621d8c3.zip"
|
||||||
|
self.act_md5 = "8f71a58f3e54eca879a2f7de64dbed58"
|
||||||
|
|
||||||
|
def download(self):
|
||||||
|
print_color("Downloading libhoudini_hack now .....", bcolors.GREEN)
|
||||||
|
super().download()
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
run(["chmod", "+x", self.extract_to, "-R"])
|
||||||
|
|
||||||
|
print_color("Copying libhoudini hack files ...", bcolors.GREEN)
|
||||||
|
name = re.findall(r"([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||||
|
shutil.copytree(os.path.join(self.extract_to, "redroid_libhoudini_hack-" + name,
|
||||||
|
self.version), os.path.join(self.copy_dir, "system"), dirs_exist_ok=True)
|
||||||
|
|
||||||
|
if not self.version == "9.0.0":
|
||||||
|
init_path = os.path.join(self.copy_dir, "system", "etc", "init", "hw", "init.rc")
|
||||||
|
os.chmod(init_path, 0o644)
|
||||||
275
stuff/magisk.py
Normal file
275
stuff/magisk.py
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import re
|
||||||
|
import zipfile
|
||||||
|
import requests
|
||||||
|
from stuff.general import General
|
||||||
|
from tools.helper import bcolors, download_file, host, print_color, run, get_download_dir
|
||||||
|
|
||||||
|
class Magisk(General):
|
||||||
|
stable_json = "https://raw.githubusercontent.com/os-fork/HuskyDG-magisk-files/main/stable.json"
|
||||||
|
machine = host()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.download_loc = get_download_dir()
|
||||||
|
self.dl_file_name = os.path.join(self.download_loc, "magisk.apk")
|
||||||
|
self.release_meta_file = os.path.join(self.download_loc, "magisk.version")
|
||||||
|
self.extract_to = os.path.join(self.download_loc, "magisk_unpack")
|
||||||
|
self.copy_dir = "./magisk"
|
||||||
|
self.magisk_dir = os.path.join(self.copy_dir, "vendor", "etc", "magisk")
|
||||||
|
self.rc_dir = os.path.join(self.copy_dir, "vendor", "etc", "init")
|
||||||
|
self.app_package = os.environ.get("REDROID_MAGISK_APP_PACKAGE", "io.github.huskydg.magisk")
|
||||||
|
self.custom_apk = os.environ.get("REDROID_MAGISK_APK", "").strip()
|
||||||
|
self.custom_url = os.environ.get("REDROID_MAGISK_URL", "").strip()
|
||||||
|
self.boot_complete_enabled = os.environ.get("REDROID_MAGISK_BOOT_COMPLETE", "0").strip().lower() in {
|
||||||
|
"1",
|
||||||
|
"true",
|
||||||
|
"yes",
|
||||||
|
"on",
|
||||||
|
}
|
||||||
|
self.preseed_sql = os.environ.get(
|
||||||
|
"REDROID_MAGISK_PRESEED_SQL",
|
||||||
|
"REPLACE INTO settings (key,value) VALUES "
|
||||||
|
"('root_access',3),('su_biometric',0),('multiuser_mode',0),('mnt_ns',1),"
|
||||||
|
"('denylist',0),('sulist',0),('zygisk',1); "
|
||||||
|
"REPLACE INTO policies (uid,policy,until,logging,notification) VALUES "
|
||||||
|
"(2000,2,0,0,0);",
|
||||||
|
).strip()
|
||||||
|
self.release_version = "unknown"
|
||||||
|
self.magisk_tmp = "/debug_ramdisk"
|
||||||
|
self.magisk_bootstrap = "magisk64" if self.machine[1] == 64 else "magisk32"
|
||||||
|
self.magisk_system_dir = "/vendor/etc/magisk"
|
||||||
|
boot_complete_component = ""
|
||||||
|
if self.boot_complete_enabled:
|
||||||
|
boot_complete_component = """
|
||||||
|
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --boot-complete""".format(
|
||||||
|
MAGISKTMP=self.magisk_tmp,
|
||||||
|
)
|
||||||
|
preseed_component = ""
|
||||||
|
if self.preseed_sql:
|
||||||
|
escaped_sql = self.preseed_sql.replace("\\", "\\\\").replace('"', '\\"')
|
||||||
|
preseed_component = """
|
||||||
|
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --sqlite "{PRESEED_SQL}" """.format(
|
||||||
|
MAGISKTMP=self.magisk_tmp,
|
||||||
|
PRESEED_SQL=escaped_sql,
|
||||||
|
)
|
||||||
|
self.bootanim_component = """
|
||||||
|
on post-fs-data
|
||||||
|
start logd
|
||||||
|
mount tmpfs tmpfs {MAGISKTMP} mode=1777
|
||||||
|
mkdir /data/adb 0700
|
||||||
|
mkdir /data/adb/magisk 0755
|
||||||
|
copy {MAGISKSYSTEMDIR}/busybox /data/adb/magisk/busybox
|
||||||
|
copy {MAGISKSYSTEMDIR}/magiskboot /data/adb/magisk/magiskboot
|
||||||
|
copy {MAGISKSYSTEMDIR}/magiskinit /data/adb/magisk/magiskinit
|
||||||
|
copy {MAGISKSYSTEMDIR}/magiskpolicy /data/adb/magisk/magiskpolicy
|
||||||
|
copy {MAGISKSYSTEMDIR}/util_functions.sh /data/adb/magisk/util_functions.sh
|
||||||
|
copy {MAGISKSYSTEMDIR}/boot_patch.sh /data/adb/magisk/boot_patch.sh
|
||||||
|
copy {MAGISKSYSTEMDIR}/addon.d.sh /data/adb/magisk/addon.d.sh
|
||||||
|
chmod 0755 /data/adb/magisk/busybox
|
||||||
|
chmod 0755 /data/adb/magisk/magiskboot
|
||||||
|
chmod 0755 /data/adb/magisk/magiskinit
|
||||||
|
chmod 0755 /data/adb/magisk/magiskpolicy
|
||||||
|
chmod 0644 /data/adb/magisk/util_functions.sh
|
||||||
|
chmod 0644 /data/adb/magisk/boot_patch.sh
|
||||||
|
chmod 0644 /data/adb/magisk/addon.d.sh
|
||||||
|
copy {MAGISKSYSTEMDIR}/magisk32 {MAGISKTMP}/magisk32
|
||||||
|
copy {MAGISKSYSTEMDIR}/magisk64 {MAGISKTMP}/magisk64
|
||||||
|
copy {MAGISKSYSTEMDIR}/magiskpolicy {MAGISKTMP}/magiskpolicy
|
||||||
|
copy {MAGISKSYSTEMDIR}/stub.apk {MAGISKTMP}/stub.apk
|
||||||
|
chmod 0755 {MAGISKTMP}/magisk32
|
||||||
|
chmod 0755 {MAGISKTMP}/magisk64
|
||||||
|
chmod 0755 {MAGISKTMP}/magiskpolicy
|
||||||
|
symlink {MAGISKTMP}/{magisk_bootstrap} {MAGISKTMP}/magisk
|
||||||
|
symlink {MAGISKTMP}/magisk {MAGISKTMP}/su
|
||||||
|
symlink {MAGISKTMP}/magisk {MAGISKTMP}/resetprop
|
||||||
|
symlink {MAGISKTMP}/magiskpolicy {MAGISKTMP}/supolicy
|
||||||
|
rm /vendor/bin/su
|
||||||
|
rm /vendor/bin/magisk
|
||||||
|
rm /vendor/bin/resetprop
|
||||||
|
rm /vendor/bin/magiskpolicy
|
||||||
|
symlink {MAGISKTMP}/magisk /vendor/bin/su
|
||||||
|
symlink {MAGISKTMP}/magisk /vendor/bin/magisk
|
||||||
|
symlink {MAGISKTMP}/magisk /vendor/bin/resetprop
|
||||||
|
symlink {MAGISKTMP}/magiskpolicy /vendor/bin/magiskpolicy
|
||||||
|
mkdir {MAGISKTMP}/.magisk 0711
|
||||||
|
mkdir {MAGISKTMP}/.magisk/device 0711
|
||||||
|
mkdir {MAGISKTMP}/.magisk/mirror 0755
|
||||||
|
mkdir {MAGISKTMP}/.magisk/block 0755
|
||||||
|
mkdir {MAGISKTMP}/.magisk/worker 0755
|
||||||
|
mkdir {MAGISKTMP}/.magisk/rootdir 0755
|
||||||
|
copy {MAGISKSYSTEMDIR}/config {MAGISKTMP}/.magisk/config
|
||||||
|
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --post-fs-data
|
||||||
|
|
||||||
|
on nonencrypted
|
||||||
|
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --service
|
||||||
|
|
||||||
|
on property:vold.decrypt=trigger_restart_framework
|
||||||
|
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --service
|
||||||
|
|
||||||
|
on property:sys.boot_completed=1
|
||||||
|
mkdir /data/adb/magisk 755
|
||||||
|
{PRESEED}
|
||||||
|
{BOOTCOMPLETE}
|
||||||
|
exec -- /system/bin/sh -c "if [ ! -e /data/data/{app_package} ] ; then pm install {MAGISKSYSTEMDIR}/magisk.apk ; fi"
|
||||||
|
|
||||||
|
on property:init.svc.zygote=restarting
|
||||||
|
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --zygote-restart
|
||||||
|
|
||||||
|
on property:init.svc.zygote=stopped
|
||||||
|
exec u:r:su:s0 root root -- {MAGISKTMP}/magisk --zygote-restart
|
||||||
|
""".format(
|
||||||
|
MAGISKSYSTEMDIR=self.magisk_system_dir,
|
||||||
|
MAGISKTMP=self.magisk_tmp,
|
||||||
|
PRESEED=preseed_component,
|
||||||
|
BOOTCOMPLETE=boot_complete_component,
|
||||||
|
magisk_bootstrap=self.magisk_bootstrap,
|
||||||
|
app_package=self.app_package,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _cached_version(self):
|
||||||
|
if os.path.isfile(self.release_meta_file):
|
||||||
|
with open(self.release_meta_file, "r", encoding="utf-8") as meta_file:
|
||||||
|
return meta_file.read().strip()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def _write_version(self):
|
||||||
|
with open(self.release_meta_file, "w", encoding="utf-8") as meta_file:
|
||||||
|
meta_file.write(self.release_version)
|
||||||
|
|
||||||
|
def _validate_archive(self):
|
||||||
|
if not zipfile.is_zipfile(self.dl_file_name):
|
||||||
|
raise ValueError(
|
||||||
|
"Downloaded Magisk/Kitsune archive is not a valid APK/ZIP. "
|
||||||
|
"Set REDROID_MAGISK_APK to a local APK or REDROID_MAGISK_URL to a working direct APK URL."
|
||||||
|
)
|
||||||
|
|
||||||
|
def resolve_release(self):
|
||||||
|
print_color("Resolving Kitsune Mask stable release ...", bcolors.GREEN)
|
||||||
|
response = requests.get(self.stable_json, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
payload = response.json()
|
||||||
|
magisk_info = payload["magisk"]
|
||||||
|
self.dl_link = magisk_info["link"]
|
||||||
|
self.release_version = magisk_info.get("version", "unknown")
|
||||||
|
|
||||||
|
def download(self):
|
||||||
|
if self.custom_apk:
|
||||||
|
self.release_version = "local:" + os.path.basename(self.custom_apk)
|
||||||
|
if (
|
||||||
|
os.path.isfile(self.dl_file_name)
|
||||||
|
and self._cached_version() == self.release_version
|
||||||
|
):
|
||||||
|
print_color(
|
||||||
|
"Using cached local Magisk/Kitsune APK {} ...".format(self.release_version),
|
||||||
|
bcolors.GREEN,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print_color(
|
||||||
|
"Copying local Magisk/Kitsune APK {} ...".format(self.custom_apk),
|
||||||
|
bcolors.GREEN,
|
||||||
|
)
|
||||||
|
shutil.copyfile(self.custom_apk, self.dl_file_name)
|
||||||
|
self._write_version()
|
||||||
|
self._validate_archive()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.custom_url:
|
||||||
|
self.dl_link = self.custom_url
|
||||||
|
self.release_version = "url:" + os.path.basename(self.custom_url)
|
||||||
|
else:
|
||||||
|
self.resolve_release()
|
||||||
|
|
||||||
|
if (
|
||||||
|
os.path.isfile(self.dl_file_name)
|
||||||
|
and self._cached_version() == self.release_version
|
||||||
|
):
|
||||||
|
print_color(
|
||||||
|
"Using cached Kitsune Mask {} ...".format(self.release_version),
|
||||||
|
bcolors.GREEN,
|
||||||
|
)
|
||||||
|
self._validate_archive()
|
||||||
|
return
|
||||||
|
|
||||||
|
print_color(
|
||||||
|
"Downloading Kitsune Mask {} now .....".format(self.release_version),
|
||||||
|
bcolors.GREEN,
|
||||||
|
)
|
||||||
|
if os.path.isfile(self.dl_file_name):
|
||||||
|
os.remove(self.dl_file_name)
|
||||||
|
download_file(self.dl_link, self.dl_file_name)
|
||||||
|
self._validate_archive()
|
||||||
|
self._write_version()
|
||||||
|
|
||||||
|
def extract(self):
|
||||||
|
print_color("Extracting archive...", bcolors.GREEN)
|
||||||
|
if os.path.exists(self.extract_to):
|
||||||
|
shutil.rmtree(self.extract_to)
|
||||||
|
os.makedirs(self.extract_to, exist_ok=True)
|
||||||
|
with zipfile.ZipFile(self.dl_file_name) as zip_handle:
|
||||||
|
zip_handle.extractall(self.extract_to)
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
if os.path.exists(self.copy_dir):
|
||||||
|
shutil.rmtree(self.copy_dir)
|
||||||
|
os.makedirs(self.magisk_dir, exist_ok=True)
|
||||||
|
os.makedirs(self.rc_dir, exist_ok=True)
|
||||||
|
os.chmod(os.path.join(self.copy_dir, "vendor"), 0o755)
|
||||||
|
os.chmod(os.path.join(self.copy_dir, "vendor", "etc"), 0o755)
|
||||||
|
os.chmod(self.magisk_dir, 0o755)
|
||||||
|
os.chmod(self.rc_dir, 0o755)
|
||||||
|
|
||||||
|
print_color("Copying magisk libs now ...", bcolors.GREEN)
|
||||||
|
|
||||||
|
arch_map = {
|
||||||
|
"x86": "x86",
|
||||||
|
"x86_64": "x86_64",
|
||||||
|
"arm": "armeabi-v7a",
|
||||||
|
"arm64": "arm64-v8a"
|
||||||
|
}
|
||||||
|
lib_dir = os.path.join(self.extract_to, "lib", arch_map[self.machine[0]])
|
||||||
|
for parent, dirnames, filenames in os.walk(lib_dir):
|
||||||
|
for filename in filenames:
|
||||||
|
o_path = os.path.join(lib_dir, filename)
|
||||||
|
filename = re.search(r'lib(.*)\.so', filename)
|
||||||
|
n_path = os.path.join(self.magisk_dir, filename.group(1))
|
||||||
|
shutil.copyfile(o_path, n_path)
|
||||||
|
os.chmod(n_path, 0o755)
|
||||||
|
|
||||||
|
if self.machine[1] == 64:
|
||||||
|
secondary_arch_map = {
|
||||||
|
"x86_64": "x86",
|
||||||
|
"arm64": "armeabi-v7a",
|
||||||
|
}
|
||||||
|
secondary_dir = os.path.join(
|
||||||
|
self.extract_to,
|
||||||
|
"lib",
|
||||||
|
secondary_arch_map[self.machine[0]],
|
||||||
|
)
|
||||||
|
magisk32_src = os.path.join(secondary_dir, "libmagisk32.so")
|
||||||
|
if os.path.exists(magisk32_src):
|
||||||
|
magisk32_dst = os.path.join(self.magisk_dir, "magisk32")
|
||||||
|
shutil.copyfile(magisk32_src, magisk32_dst)
|
||||||
|
os.chmod(magisk32_dst, 0o755)
|
||||||
|
|
||||||
|
assets_dir = os.path.join(self.extract_to, "assets")
|
||||||
|
for asset_name in ["util_functions.sh", "addon.d.sh", "boot_patch.sh", "stub.apk"]:
|
||||||
|
asset_src = os.path.join(assets_dir, asset_name)
|
||||||
|
if os.path.exists(asset_src):
|
||||||
|
asset_dst = os.path.join(self.magisk_dir, asset_name)
|
||||||
|
shutil.copyfile(asset_src, asset_dst)
|
||||||
|
os.chmod(asset_dst, 0o644)
|
||||||
|
|
||||||
|
config_path = os.path.join(self.magisk_dir, "config")
|
||||||
|
with open(config_path, "w", encoding="utf-8") as config_file:
|
||||||
|
config_file.write("SYSTEMMODE=true\nRECOVERYMODE=false\n")
|
||||||
|
os.chmod(config_path, 0o600)
|
||||||
|
|
||||||
|
magisk_apk_path = os.path.join(self.magisk_dir, "magisk.apk")
|
||||||
|
shutil.copyfile(self.dl_file_name, magisk_apk_path)
|
||||||
|
os.chmod(magisk_apk_path, 0o644)
|
||||||
|
|
||||||
|
magisk_rc_path = os.path.join(self.rc_dir, "magisk.rc")
|
||||||
|
with open(magisk_rc_path, "w", encoding="utf-8") as initfile:
|
||||||
|
initfile.write(self.bootanim_component)
|
||||||
|
os.chmod(magisk_rc_path, 0o644)
|
||||||
124
stuff/mindthegapps.py
Normal file
124
stuff/mindthegapps.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from stuff.general import General
|
||||||
|
from tools.helper import get_download_dir, host, print_color, run, bcolors
|
||||||
|
|
||||||
|
|
||||||
|
class MindTheGapps(General):
|
||||||
|
dl_links = {
|
||||||
|
"15.0.0": {
|
||||||
|
"x86_64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20250330/MindTheGapps-15.0.0-x86_64-20250330.zip",
|
||||||
|
"e54694828bd74e9066b2534a9675c31e",
|
||||||
|
],
|
||||||
|
"arm64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20250330/MindTheGapps-15.0.0-arm64-20250330.zip",
|
||||||
|
"79acb62f0f7c66b0f0bcadae5624f3d1",
|
||||||
|
],
|
||||||
|
"arm": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20250330/MindTheGapps-15.0.0-arm-20250330.zip",
|
||||||
|
"4ced6a404a714e61831e16068c3642b3",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"14.0.0": {
|
||||||
|
"x86_64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-14.0.0-x86_64-20240226.zip",
|
||||||
|
"a827a84ccb0cf5914756e8561257ed13",
|
||||||
|
],
|
||||||
|
"x86": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-14.0.0-x86-20240226.zip",
|
||||||
|
"45736b21475464e4a45196b9aa9d3b7f",
|
||||||
|
],
|
||||||
|
"arm64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-14.0.0-arm64-20240226.zip",
|
||||||
|
"a0905cc7bf3f4f4f2e3f59a4e1fc789b",
|
||||||
|
],
|
||||||
|
"arm": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-14.0.0-arm-20240226.zip",
|
||||||
|
"fa167a3b7a10c4d3e688a59cd794f75b",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"13.0.0": {
|
||||||
|
"x86_64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-13.0.0-x86_64-20240226.zip",
|
||||||
|
"eee87a540b6e778f3a114fff29e133aa",
|
||||||
|
],
|
||||||
|
"x86": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-13.0.0-x86-20240226.zip",
|
||||||
|
"d928c5eabb4394a97f2d7a5c663e7c2e",
|
||||||
|
],
|
||||||
|
"arm64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-13.0.0-arm64-20240226.zip",
|
||||||
|
"ebdf35e17bc1c22337762fcf15cd6e97",
|
||||||
|
],
|
||||||
|
"arm": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240619/MindTheGapps-13.0.0-arm-20240619.zip",
|
||||||
|
"ec7aa5efc9e449b101bc2ee7448a49bf",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"13.0.0_64only": {
|
||||||
|
"x86_64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-13.0.0-x86_64-20240226.zip",
|
||||||
|
"eee87a540b6e778f3a114fff29e133aa",
|
||||||
|
],
|
||||||
|
"arm64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240226/MindTheGapps-13.0.0-arm64-20240226.zip",
|
||||||
|
"ebdf35e17bc1c22337762fcf15cd6e97",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"12.0.0_64only": {
|
||||||
|
"x86_64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240619/MindTheGapps-12.1.0-x86_64-20240619.zip",
|
||||||
|
"05d6e99b6e6567e66d43774559b15fbd"
|
||||||
|
],
|
||||||
|
"arm64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240619/MindTheGapps-12.1.0-arm64-20240619.zip",
|
||||||
|
"94dd174ff16c2f0006b66b25025efd04",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"12.0.0": {
|
||||||
|
"x86_64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240619/MindTheGapps-12.1.0-x86_64-20240619.zip",
|
||||||
|
"05d6e99b6e6567e66d43774559b15fbd"
|
||||||
|
],
|
||||||
|
"x86": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240619/MindTheGapps-12.1.0-x86-20240619.zip",
|
||||||
|
"ff2421a75afbdda8a003e4fd25e95050"
|
||||||
|
],
|
||||||
|
"arm64": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240619/MindTheGapps-12.1.0-arm64-20240619.zip",
|
||||||
|
"94dd174ff16c2f0006b66b25025efd04",
|
||||||
|
],
|
||||||
|
"arm": [
|
||||||
|
"https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20240619/MindTheGapps-12.1.0-arm-20240619.zip",
|
||||||
|
"5af756b3b5776c2f6ee024a9f7f42a2f",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
arch = host()
|
||||||
|
download_loc = get_download_dir()
|
||||||
|
dl_file_name = os.path.join(download_loc, "mindthegapps.zip")
|
||||||
|
dl_link = ...
|
||||||
|
act_md5 = ...
|
||||||
|
copy_dir = "./mindthegapps"
|
||||||
|
extract_to = os.path.join(download_loc, "mindthegapps_extract")
|
||||||
|
|
||||||
|
def __init__(self, version):
|
||||||
|
self.version = version
|
||||||
|
self.dl_link = self.dl_links[self.version][self.arch[0]][0]
|
||||||
|
self.act_md5 = self.dl_links[self.version][self.arch[0]][1]
|
||||||
|
|
||||||
|
def download(self):
|
||||||
|
print_color("Downloading MindTheGapps now .....", bcolors.GREEN)
|
||||||
|
super().download()
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
if os.path.exists(self.copy_dir):
|
||||||
|
shutil.rmtree(self.copy_dir)
|
||||||
|
if not os.path.exists(self.extract_to):
|
||||||
|
os.makedirs(self.extract_to)
|
||||||
|
|
||||||
|
shutil.copytree(
|
||||||
|
os.path.join(self.extract_to, "system", ),
|
||||||
|
os.path.join(self.copy_dir, "system"), dirs_exist_ok=True, )
|
||||||
95
tools/helper.py
Normal file
95
tools/helper.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import requests
|
||||||
|
from tqdm import tqdm
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
def get_download_dir():
|
||||||
|
download_loc = ""
|
||||||
|
current_user = (
|
||||||
|
os.environ.get("SUDO_USER")
|
||||||
|
or os.environ.get("USER")
|
||||||
|
or os.environ.get("USERNAME")
|
||||||
|
or "user"
|
||||||
|
)
|
||||||
|
if os.environ.get("XDG_CACHE_HOME", None) is None:
|
||||||
|
if os.name == "nt":
|
||||||
|
download_loc = os.path.join(
|
||||||
|
os.environ.get("LOCALAPPDATA", os.path.expanduser("~")),
|
||||||
|
"redroid",
|
||||||
|
"downloads",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
download_loc = os.path.join(
|
||||||
|
'/',
|
||||||
|
"home",
|
||||||
|
current_user,
|
||||||
|
".cache",
|
||||||
|
"redroid",
|
||||||
|
"downloads",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
download_loc = os.path.join(os.environ["XDG_CACHE_HOME"], "redroid", "downloads")
|
||||||
|
if not os.path.exists(download_loc):
|
||||||
|
os.makedirs(download_loc)
|
||||||
|
return download_loc
|
||||||
|
|
||||||
|
def run(args):
|
||||||
|
result = subprocess.run(args=args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
if result.stderr:
|
||||||
|
print(result.stderr.decode("utf-8"))
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
returncode = result.returncode,
|
||||||
|
cmd = result.args,
|
||||||
|
stderr = result.stderr
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def download_file(url, f_name):
|
||||||
|
md5 = ""
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
total_size_in_bytes = int(response.headers.get('content-length', 0))
|
||||||
|
block_size = 1024 # 1 Kibibyte
|
||||||
|
progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True)
|
||||||
|
with open(f_name, 'wb') as file:
|
||||||
|
for data in response.iter_content(block_size):
|
||||||
|
progress_bar.update(len(data))
|
||||||
|
file.write(data)
|
||||||
|
progress_bar.close()
|
||||||
|
with open(f_name, "rb") as f:
|
||||||
|
bytes = f.read()
|
||||||
|
md5 = hashlib.md5(bytes).hexdigest()
|
||||||
|
if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes:
|
||||||
|
raise ValueError("Something went wrong while downloading")
|
||||||
|
return md5
|
||||||
|
|
||||||
|
def host():
|
||||||
|
machine = platform.machine().lower()
|
||||||
|
|
||||||
|
mapping = {
|
||||||
|
"i686": ("x86", 32),
|
||||||
|
"x86_64": ("x86_64", 64),
|
||||||
|
"amd64": ("x86_64", 64),
|
||||||
|
"aarch64": ("arm64", 64),
|
||||||
|
"armv7l": ("arm", 32),
|
||||||
|
"armv8l": ("arm", 32)
|
||||||
|
}
|
||||||
|
if machine in mapping:
|
||||||
|
# if mapping[machine] == "x86_64":
|
||||||
|
# with open("/proc/cpuinfo") as f:
|
||||||
|
# if "sse4_2" not in f.read():
|
||||||
|
# print("x86_64 CPU does not support SSE4.2, falling back to x86...")
|
||||||
|
# return ("x86", 32)
|
||||||
|
return mapping[machine]
|
||||||
|
raise ValueError("platform.machine '" + machine + "'"
|
||||||
|
" architecture is not supported")
|
||||||
|
|
||||||
|
class bcolors:
|
||||||
|
RED = '\033[31m'
|
||||||
|
YELLOW = '\033[33m'
|
||||||
|
GREEN = '\033[32m'
|
||||||
|
ENDC = '\033[0m'
|
||||||
|
|
||||||
|
def print_color(str, color):
|
||||||
|
print(color+str+bcolors.ENDC)
|
||||||
Reference in New Issue
Block a user