Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b20483dd9a | ||
|
|
8020dbd8fc | ||
|
|
b7e1c37e14 | ||
|
|
2f6547f873 | ||
|
|
48dbfaf34a | ||
|
|
b09e018894 | ||
|
|
3bc2b310a3 | ||
|
|
963e3511fe | ||
|
|
d5289cfd89 | ||
|
|
ddaa6b190f | ||
|
|
15020ec20a | ||
|
|
fcb15624db | ||
|
|
db43c7c9cf | ||
|
|
3e344b360f | ||
|
|
5527d390eb | ||
|
|
6f44d95070 | ||
|
|
32d21c8074 | ||
|
|
eb337a1351 | ||
|
|
48186df8af | ||
|
|
c14650010e | ||
|
|
2d3bd9432f | ||
|
|
1a2d3ad643 | ||
|
|
7cccf2b56d | ||
|
|
d31988a829 | ||
|
|
b12c06568c | ||
|
|
e0de64c047 | ||
|
|
bc3efd48c2 | ||
|
|
cd6239737a | ||
|
|
97293f3cc1 | ||
|
|
76caf7e1d7 | ||
|
|
3527282950 | ||
|
|
d2502086ed | ||
|
|
c5110830f7 | ||
|
|
d81b72fed7 | ||
|
|
bd8d53906a | ||
|
|
acbb764ac5 | ||
|
|
45602d27c8 | ||
|
|
6706f4b9d7 | ||
|
|
b1c88db1ce | ||
|
|
688c84fd58 | ||
|
|
367697d59e | ||
|
|
77e222fb16 | ||
|
|
821c7c0bc2 | ||
|
|
854b4b0a6c | ||
|
|
6d5af57ebc | ||
|
|
a80ff8e855 | ||
|
|
3e51f7f56e | ||
|
|
e1ee7f0b0c | ||
|
|
337d066eeb | ||
|
|
4db9bb3cba | ||
|
|
489159c5f9 | ||
|
|
9c2d8fcd83 | ||
|
|
098036cca9 | ||
|
|
212135d3c8 | ||
|
|
7b5c7f64c0 | ||
|
|
b80dcf171f | ||
|
|
59547e7743 | ||
|
|
ddcc581a9c | ||
|
|
6db853e8d1 | ||
|
|
cb206cc8c0 | ||
|
|
910291ceb9 | ||
|
|
2e693d03db | ||
|
|
4777296b34 | ||
|
|
cb9d2b4726 | ||
|
|
6c6e651d79 | ||
|
|
4fce5db93e | ||
|
|
0ab4b1c0f7 | ||
|
|
ab334ebfc3 | ||
|
|
4070ab8eb9 | ||
|
|
86aabfb2e2 | ||
|
|
7fcfe62e66 | ||
|
|
3900328b83 | ||
|
|
48ea54fc3e | ||
|
|
6922bdba0d | ||
|
|
00714a5c0e | ||
|
|
ee8b9ef213 | ||
|
|
c48b1df5cd | ||
|
|
0b2afba187 | ||
|
|
43d8cfb18d | ||
|
|
e305dd1dc4 | ||
|
|
556f277fec | ||
|
|
7c31b838f6 | ||
|
|
8c66e7cf34 | ||
|
|
5f6fc21c38 | ||
|
|
d72bf018d1 | ||
|
|
0feb86ceef | ||
|
|
553f48fa27 | ||
|
|
53a4bad536 | ||
|
|
ca932b90e1 | ||
|
|
0d7681bccb | ||
|
|
4d15b6153c | ||
|
|
9d9f504625 | ||
|
|
0bc2cedc2d | ||
|
|
d205812acb | ||
|
|
fa9e89e868 | ||
|
|
72e34c4a38 | ||
|
|
92f7e98749 | ||
|
|
dbf630ba7e | ||
|
|
59ad317684 | ||
|
|
0abfc4ef4b | ||
|
|
4f6410c3bb | ||
|
|
a621645e07 | ||
|
|
545e733566 | ||
|
|
771b8d7224 | ||
|
|
a701ca8027 | ||
|
|
abdc3b015d | ||
|
|
d770a95995 | ||
|
|
1caafe57e6 | ||
|
|
6c78b793c8 | ||
|
|
6b4bc0da47 | ||
|
|
afa0180032 | ||
|
|
2f4f056fb1 | ||
|
|
97020f99c1 | ||
|
|
62749bbb8e | ||
|
|
8f8691423b | ||
|
|
90fff773b2 | ||
|
|
62a460b2c3 | ||
|
|
da15751629 | ||
|
|
df46ba8fed | ||
|
|
4681bb48fe | ||
|
|
7a27b8c193 | ||
|
|
325dbb472a | ||
|
|
4b83449e3b | ||
|
|
43311707a3 | ||
|
|
37c278dc52 | ||
|
|
af29dd0ddf | ||
|
|
faa7550c6a | ||
|
|
9f4a1280ce | ||
|
|
d27de7d4b5 | ||
|
|
15b1ac62ff | ||
|
|
7b70434f50 | ||
|
|
c19183f544 | ||
|
|
e77cd523a0 | ||
|
|
5453fae828 | ||
|
|
8f1b6a2712 | ||
|
|
e765a0c880 | ||
|
|
2ac0285722 | ||
|
|
cbcd5365c8 | ||
|
|
9af33d60f4 | ||
|
|
5cd4f746d6 | ||
|
|
6b4ab03ba3 | ||
|
|
3cb971a4ae | ||
|
|
2bd9ca5d5a | ||
|
|
e69aadaa94 | ||
|
|
dce577b931 | ||
|
|
472de72e50 | ||
|
|
b90c148229 | ||
|
|
1995d57e21 | ||
|
|
45d5dbd12d | ||
|
|
a6dda23325 | ||
|
|
98f957626b | ||
|
|
4736a61136 | ||
|
|
2ffd72c7d3 | ||
|
|
4849abf9cc | ||
|
|
c207028677 | ||
|
|
fd732b5b03 | ||
|
|
be97f33bcd | ||
|
|
de2b5597e1 | ||
|
|
aab34beaca | ||
|
|
4bdb95aee0 | ||
|
|
44ffa7152b | ||
|
|
c91ab576e2 | ||
|
|
8f28a3723d | ||
|
|
b9390a7ebc | ||
|
|
8ad6bb8283 | ||
|
|
9e6c8fd67f | ||
|
|
05f84916e7 | ||
|
|
e496d2ece8 | ||
|
|
1c6e6456e6 | ||
|
|
f49918dab6 | ||
|
|
02811ad01d |
@@ -0,0 +1,111 @@
|
||||
git
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
*.out
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
.vscode
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
.idea/
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
workspace.xml
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
.idea/workspace.xml
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
# nix
|
||||
result*
|
||||
|
||||
test.py
|
||||
@@ -1,78 +1,205 @@
|
||||
# Waydroid Extras Script
|
||||
|
||||
[](https://github.com/casualsnek)
|
||||
|
||||
Script to add gapps and other stuff to waydroid !
|
||||
Script to add GApps and other stuff to Waydroid!
|
||||
|
||||
# Installation/Usage
|
||||
"lzip" and "sqlite" is required for this script to work, install it using your distribution's package manager:
|
||||
## Arch, Manjaro and EndeavourOS based distributions:
|
||||
sudo pacman -S lzip sqlite
|
||||
## Debian and Ubuntu based distributions:
|
||||
sudo apt install lzip sqlite
|
||||
## RHEL, Fedora and Rocky based distributions:
|
||||
sudo dnf install lzip sqlite
|
||||
## openSUSE based distributions:
|
||||
sudo zypper install lzip sqlite
|
||||
Then run:
|
||||
|
||||
## Interactive terminal interface
|
||||
|
||||
```
|
||||
git clone https://github.com/casualsnek/waydroid_script
|
||||
cd waydroid_script
|
||||
sudo python3 -m pip install -r requirements.txt
|
||||
sudo python3 waydroid_extras.py [-i/-g/-n/-h]
|
||||
python3 -m venv venv
|
||||
venv/bin/pip install -r requirements.txt
|
||||
sudo venv/bin/python3 main.py
|
||||
```
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## Command Line
|
||||
|
||||
```bash
|
||||
git clone https://github.com/casualsnek/waydroid_script
|
||||
cd waydroid_script
|
||||
python3 -m venv venv
|
||||
venv/bin/pip install -r requirements.txt
|
||||
# install something
|
||||
sudo venv/bin/python3 main.py install {gapps, magisk, libndk, libhoudini, nodataperm, smartdock, microg, mitm}
|
||||
# uninstall something
|
||||
sudo venv/bin/python3 main.py uninstall {gapps, magisk, libndk, libhoudini, nodataperm, smartdock, microg}
|
||||
# get Android device ID
|
||||
sudo venv/bin/python3 main.py certified
|
||||
# some hacks
|
||||
sudo venv/bin/python3 main.py hack {nodataperm, hidestatusbar}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
"lzip" is required for this script to work, install it using your distribution's package manager:
|
||||
### Arch, Manjaro and EndeavourOS based distributions:
|
||||
sudo pacman -S lzip
|
||||
### Debian and Ubuntu based distributions:
|
||||
sudo apt install lzip
|
||||
### RHEL, Fedora and Rocky based distributions:
|
||||
sudo dnf install lzip
|
||||
### openSUSE based distributions:
|
||||
sudo zypper install lzip
|
||||
|
||||
## Install OpenGapps
|
||||
|
||||
Open terminal and switch to directory where "waydroid_extras.py" is located then run:
|
||||

|
||||
|
||||
sudo python3 waydroid_extras.py -g
|
||||
Then restart waydroid service with command below :
|
||||
Open terminal and switch to the directory where "main.py" is located then run:
|
||||
|
||||
sudo venv/bin/python3 main.py install gapps
|
||||
|
||||
sudo systemctl start waydroid-container.service
|
||||
Then launch waydroid with:
|
||||
|
||||
waydroid show-full-ui
|
||||
After waydroid has finished booting open terminal and switch to directory where "waydroid_extras.py" is located then run:
|
||||
|
||||
sudo python3 waydroid_extras.py -i
|
||||
Copy the returned numeric ID open ["https://google.com/android/uncertified/?pli=1"](https://google.com/android/uncertified/?pli=1) enter the id and register it, you may need to wait upto 10-20 minutes for device to get registered, then clear Google Play Service's cache and try logging in !
|
||||
After waydroid has finished booting, open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo python3 main.py google
|
||||
Copy the returned numeric ID, then open ["https://google.com/android/uncertified/?pli=1"](https://google.com/android/uncertified/?pli=1). Enter the ID and register it. Wait 10-20 minutes for device to get registered. Then clear Google Play Service's cache and try logging in!
|
||||
|
||||
|
||||
## Install Magisk
|
||||
|
||||
Open terminal and switch to directory where "waydroid_extras.py" is located then run:
|
||||

|
||||
|
||||
sudo python3 waydroid_extras.py -m
|
||||
Then restart waydroid service with command below :
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo venv/bin/python3 main.py install magisk
|
||||
|
||||
sudo systemctl start waydroid-container.service
|
||||
Magisk will be installed on next boot!
|
||||
Note That this is bootless installation and modules don't work as of now !
|
||||
|
||||
Zygisk and modules like LSPosed should work now.
|
||||
|
||||
If you want to update Magisk, Please use `Direct Install into system partition` or run this sript again.
|
||||
|
||||
This script only focuses on Magisk installation, if you need more management, please check https://github.com/nitanmarcel/waydroid-magisk
|
||||
|
||||
## Install libndk arm translation
|
||||
|
||||
This may or may not work properly and is only for testing:
|
||||
Open terminal and switch to directory where "waydroid_extras.py" is located then run:
|
||||
libndk_translation from guybrush firmware.
|
||||
|
||||
sudo python3 waydroid_extras.py -n
|
||||
Then restart waydroid service with command below :
|
||||
libndk seems to have better performance than libhoudini on AMD.
|
||||
|
||||
sudo systemctl start waydroid-container.service
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo venv/bin/python3 main.py install libndk
|
||||
|
||||
## Install libhoudini arm translation
|
||||
|
||||
This may or may not work properly and is only for testing: ( Much stable than libndk ) and works on android 11 images
|
||||
Open terminal and switch to directory where "waydroid_extras.py" is located then run:
|
||||
Intel's libhoudini for intel/AMD x86 CPU, pulled from Microsoft's WSA 11 image
|
||||
|
||||
sudo python3 waydroid_extras.py -l
|
||||
Then restart waydroid service with command below :
|
||||
houdini version: 11.0.1b_y.38765.m
|
||||
|
||||
houdini64 version: 11.0.1b_z.38765.m
|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo venv/bin/python3 main.py install libhoudini
|
||||
|
||||
## Integrate Widevine DRM (L3)
|
||||
|
||||

|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo venv/bin/python3 main.py install widevine
|
||||
|
||||
## Install Smart Dock
|
||||
|
||||

|
||||

|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo venv/bin/python3 main.py install smartdock
|
||||
|
||||
## Install a self-signed CA certificate
|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo venv/bin/python3 main.py install mitm --ca-cert mycert.pem
|
||||
|
||||
## Granting full permission for apps data (HACK)
|
||||
|
||||
|
||||
This is a temporary hack to combat against the apps permission issue on Android 11. Whenever an app is open it will always enable a property (persist.sys.nodataperm) to make it execute a script to grant the data full permissions (777). The **correct** way is to use `sdcardfs` or `esdfs`, both need to recompile the kernel or WayDroid image.
|
||||
|
||||
Arknights, PUNISHING: GRAY RAVEN and other games won't freeze on the black screen.
|
||||
|
||||

|
||||
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
```
|
||||
sudo venv/bin/python3 main.py hack nodataperm
|
||||
```
|
||||
**WARNING**: Tested on `lineage-18.1-20230128-VANILLA-waydroid_x86_64.img`. This script will replace `/system/framework/service.jar`, which may prevent WayDroid from booting. If so, run `sudo venv/bin/python3 main.py uninstall nodataperm` to remove it.
|
||||
|
||||
|
||||
Or you can run the following commands directly in `sudo waydroid shell`. In this way, every time a new game is installed, you need to run it again, but it's much less risky.
|
||||
|
||||
```
|
||||
chmod 777 -R /sdcard/Android
|
||||
chmod 777 -R /data/media/0/Android
|
||||
chmod 777 -R /sdcard/Android/data
|
||||
chmod 777 -R /data/media/0/Android/obb
|
||||
chmod 777 -R /mnt/*/*/*/*/Android/data
|
||||
chmod 777 -R /mnt/*/*/*/*/Android/obb
|
||||
```
|
||||
|
||||
- https://github.com/supremegamers/device_generic_common/commit/2d47891376c96011b2ee3c1ccef61cb48e15aed6
|
||||
- https://github.com/supremegamers/android_frameworks_base/commit/24a08bf800b2e461356a9d67d04572bb10b0e819
|
||||
|
||||
## Install microG, Aurora Store and Aurora Droid
|
||||
|
||||

|
||||
|
||||
```
|
||||
sudo venv/bin/python3 main.py install microg
|
||||
```
|
||||
|
||||
## Hide Status Bar
|
||||
Before
|
||||

|
||||
|
||||
After
|
||||

|
||||
|
||||
```
|
||||
sudo venv/bin/python3 main.py hack hidestatusbar
|
||||
```
|
||||
|
||||
sudo systemctl start waydroid-container.service
|
||||
|
||||
## Get Android ID for device registration
|
||||
|
||||
You need to register you device with its it before being able to use gapps, this will print out your Android ID which you can use for device registration required for google apps:
|
||||
Open terminal and switch to directory where "waydroid_extras.py" is located then run:
|
||||
You need to register you device with its it before being able to use gapps, this will print out your Android ID which you can use for device registration required for Google apps:
|
||||
Open terminal and switch to directory where "main.py" is located then run:
|
||||
|
||||
sudo python3 waydroid_extras.py -i
|
||||
sudo venv/bin/python3 main.py certified
|
||||
|
||||
Star this repository if you find this useful, if you encounter problem create a issue on github !
|
||||
Star this repository if you find this useful, if you encounter problem create an issue on GitHub!
|
||||
|
||||
## Error handling
|
||||
|
||||
- Magisk installed: N/A
|
||||
|
||||
Check [waydroid-magisk](https://github.com/nitanmarcel/waydroid-magisk)
|
||||
|
||||
## Credits
|
||||
- [WayDroid](https://github.com/waydroid/waydroid)
|
||||
- [Magisk Delta](https://huskydg.github.io/magisk-files/)
|
||||
- [microG Project](https://microg.org)
|
||||
- [Open GApps](https://opengapps.org)
|
||||
- [Smart Dock](https://github.com/axel358/smartdock)
|
||||
- [wd-scripts](https://github.com/electrikjesus/wd-scripts/)
|
||||
|
||||
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 321 KiB |
|
After Width: | Height: | Size: 348 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 590 KiB |
|
After Width: | Height: | Size: 665 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 43 KiB |
@@ -0,0 +1,4 @@
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
in
|
||||
pkgs.callPackage ./package.nix { }
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1707650010,
|
||||
"narHash": "sha256-dOhphIA4MGrH4ElNCy/OlwmN24MsnEqFjRR6+RY7jZw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "809cca784b9f72a5ad4b991e0e7bcf8890f9c3a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
description = "Waydroid Extras Script";
|
||||
inputs = {
|
||||
systems.url = "github:nix-systems/default";
|
||||
};
|
||||
outputs = { self, nixpkgs, systems }:
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
eachSystem = lib.genAttrs (import systems);
|
||||
mkApp = program: { type = "app"; inherit program; };
|
||||
in {
|
||||
packages = eachSystem (system: rec {
|
||||
waydroid_script = nixpkgs.legacyPackages."${system}".callPackage ./package.nix { };
|
||||
default = waydroid_script;
|
||||
});
|
||||
apps = eachSystem (system: rec {
|
||||
waydroid_script = mkApp "${self.outputs.packages.${system}.waydroid_script}/bin/waydroid_script";
|
||||
default = waydroid_script;
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
#!/usr/bin/env python3
|
||||
from InquirerPy import inquirer
|
||||
from InquirerPy.base.control import Choice
|
||||
from InquirerPy.separator import Separator
|
||||
import argparse
|
||||
import os
|
||||
from typing import List
|
||||
from stuff.android_id import AndroidId
|
||||
from stuff.gapps import Gapps
|
||||
from stuff.general import General
|
||||
from stuff.hidestatusbar import HideStatusBar
|
||||
from stuff.houdini import Houdini
|
||||
from stuff.magisk import Magisk
|
||||
from stuff.microg import MicroG
|
||||
from stuff.mitm import Mitm
|
||||
from stuff.ndk import Ndk
|
||||
from stuff.nodataperm import Nodataperm
|
||||
from stuff.smartdock import Smartdock
|
||||
from stuff.widevine import Widevine
|
||||
from stuff.fdroidpriv import FDroidPriv
|
||||
from stuff.device_spoof import DeviceSpoof, PROFILES
|
||||
import tools.helper as helper
|
||||
from tools import container
|
||||
from tools import images
|
||||
|
||||
import argparse
|
||||
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
def get_certified(args):
|
||||
AndroidId().get_id()
|
||||
|
||||
|
||||
def mount(partition, copy_dir):
|
||||
img = os.path.join(images.get_image_dir(), partition+".img")
|
||||
mount_point = ""
|
||||
if partition == "system":
|
||||
mount_point = os.path.join(copy_dir)
|
||||
else:
|
||||
mount_point = os.path.join(copy_dir, partition)
|
||||
Logger.info("Mounting {} to {}".format(img, mount_point))
|
||||
images.mount(img, mount_point)
|
||||
|
||||
|
||||
def resize(partition):
|
||||
img = os.path.join(images.get_image_dir(), partition+".img")
|
||||
img_size = int(os.path.getsize(img)/(1024*1024))
|
||||
new_size = "{}M".format(img_size+500)
|
||||
Logger.info("Resizing {} to {}".format(img, new_size))
|
||||
images.resize(img, new_size)
|
||||
|
||||
|
||||
def umount(partition, copy_dir):
|
||||
mount_point = ""
|
||||
if partition == "system":
|
||||
mount_point = os.path.join(copy_dir)
|
||||
else:
|
||||
mount_point = os.path.join(copy_dir, partition)
|
||||
Logger.info("Umounting {}".format(mount_point))
|
||||
images.umount(mount_point)
|
||||
|
||||
|
||||
def install_app(args):
|
||||
install_list: List[General] = []
|
||||
app = args.app
|
||||
if "gapps" in app:
|
||||
install_list.append(Gapps(args.android_version))
|
||||
if "libndk" in app and "houdini" not in app:
|
||||
arch = helper.host()[0]
|
||||
if arch == "x86_64":
|
||||
install_list.append(Ndk(args.android_version))
|
||||
else:
|
||||
Logger.warn("libndk is not supported on your CPU")
|
||||
if "libhoudini" in app and "ndk" not in app:
|
||||
arch = helper.host()[0]
|
||||
if arch == "x86_64":
|
||||
install_list.append(Houdini(args.android_version))
|
||||
else:
|
||||
Logger.warn("libhoudini is not supported on your CPU")
|
||||
if "magisk" in app:
|
||||
install_list.append(Magisk())
|
||||
if "widevine" in app:
|
||||
install_list.append(Widevine(args.android_version))
|
||||
if "smartdock" in app:
|
||||
install_list.append(Smartdock())
|
||||
if "microg" in app:
|
||||
install_list.append(MicroG(args.android_version, args.microg_variant))
|
||||
if "mitm" in app:
|
||||
install_list.append(Mitm(args.ca_cert_file))
|
||||
if "fdroidpriv" in app:
|
||||
install_list.append(FDroidPriv(args.android_version))
|
||||
if "device_spoof" in app:
|
||||
DeviceSpoof(getattr(args, "spoof_profile", "pixel6")).install()
|
||||
|
||||
if not container.use_overlayfs():
|
||||
copy_dir = "/tmp/waydroid"
|
||||
container.stop()
|
||||
|
||||
resize_system, resize_vendor = False, False
|
||||
for item in install_list:
|
||||
if item.partition == "system":
|
||||
resize_system = True
|
||||
elif item.partition == "vendor":
|
||||
resize_vendor = True
|
||||
|
||||
if resize_system:
|
||||
resize("system")
|
||||
if resize_vendor:
|
||||
resize("vendor")
|
||||
|
||||
mount("system", copy_dir)
|
||||
mount("vendor", copy_dir)
|
||||
|
||||
for item in install_list:
|
||||
item.install()
|
||||
|
||||
if not container.use_overlayfs():
|
||||
umount("vendor", copy_dir)
|
||||
umount("system", copy_dir)
|
||||
|
||||
container.upgrade()
|
||||
|
||||
|
||||
def remove_app(args):
|
||||
remove_list: List[General] = []
|
||||
app = args.app
|
||||
if "gapps" in app:
|
||||
remove_list.append(Gapps(args.android_version))
|
||||
if "libndk" in app and "houdini" not in app:
|
||||
remove_list.append(Ndk(args.android_version))
|
||||
if "libhoudini" in app and "ndk" not in app:
|
||||
remove_list.append(Houdini(args.android_version))
|
||||
if "magisk" in app:
|
||||
remove_list.append(Magisk())
|
||||
if "widevine" in app:
|
||||
remove_list.append(Widevine(args.android_version))
|
||||
if "smartdock" in app:
|
||||
remove_list.append(Smartdock())
|
||||
if "microg" in app:
|
||||
remove_list.append(MicroG(args.android_version, args.microg_variant))
|
||||
if "mitm" in app:
|
||||
remove_list.append(Mitm())
|
||||
if "fdroidpriv" in app:
|
||||
remove_list.append(FDroidPriv(args.android_version))
|
||||
if "device_spoof" in app:
|
||||
DeviceSpoof().uninstall()
|
||||
if "nodataperm" in app:
|
||||
remove_list.append(Nodataperm(args.android_version))
|
||||
if "hidestatusbar" in app:
|
||||
remove_list.append(HideStatusBar())
|
||||
|
||||
if not container.use_overlayfs():
|
||||
copy_dir = "/tmp/waydroid"
|
||||
container.stop()
|
||||
|
||||
for item in remove_list:
|
||||
item.uninstall()
|
||||
|
||||
if not container.use_overlayfs():
|
||||
umount("vendor", copy_dir)
|
||||
umount("system", copy_dir)
|
||||
|
||||
container.upgrade()
|
||||
|
||||
|
||||
def hack_option(args):
|
||||
Logger.warning(
|
||||
"If these hacks cause any problems, run `sudo python main.py remove <hack_option>` to remove")
|
||||
|
||||
hack_list: List[General] = []
|
||||
options = args.option_name
|
||||
if "nodataperm" in options:
|
||||
hack_list.append(Nodataperm())
|
||||
if "hidestatusbar" in options:
|
||||
hack_list.append(HideStatusBar())
|
||||
|
||||
if not container.use_overlayfs():
|
||||
copy_dir = "/tmp/waydroid"
|
||||
container.stop()
|
||||
|
||||
resize_system, resize_vendor = False, False
|
||||
for item in hack_list:
|
||||
if item.partition == "system":
|
||||
resize_system = True
|
||||
elif item.partition == "vendor":
|
||||
resize_vendor = True
|
||||
|
||||
if resize_system:
|
||||
resize("system")
|
||||
if resize_vendor:
|
||||
resize("vendor")
|
||||
|
||||
mount("system", copy_dir)
|
||||
mount("vendor", copy_dir)
|
||||
|
||||
for item in hack_list:
|
||||
item.install()
|
||||
|
||||
if not container.use_overlayfs():
|
||||
umount("vendor", copy_dir)
|
||||
umount("system", copy_dir)
|
||||
|
||||
container.upgrade()
|
||||
|
||||
|
||||
def interact():
|
||||
os.system("clear")
|
||||
args = argparse.Namespace()
|
||||
android_version = inquirer.select(
|
||||
message="Select Android version",
|
||||
instruction="(\u2191\u2193 Select Item)",
|
||||
choices=[
|
||||
Choice(name="Android 11", value="11"),
|
||||
Choice(name="Android 13", value="13"),
|
||||
Choice(name="Exit", value=None)
|
||||
],
|
||||
default="13",
|
||||
).execute()
|
||||
if not android_version:
|
||||
exit()
|
||||
args.android_version = android_version
|
||||
action = inquirer.select(
|
||||
message="Please select an action",
|
||||
choices=[
|
||||
"Install",
|
||||
"Remove",
|
||||
"Hack",
|
||||
"Get Google Device ID to Get Certified"
|
||||
],
|
||||
instruction="([↑↓]: Select Item)",
|
||||
default=None,
|
||||
).execute()
|
||||
if not action:
|
||||
exit()
|
||||
|
||||
install_choices = ["gapps", "microg", "libndk", "libhoudini", "magisk", "smartdock", "fdroidpriv", "widevine", "device_spoof"]
|
||||
hack_choices = []
|
||||
if android_version=="11":
|
||||
hack_choices.extend(["nodataperm", "hidestatusbar"])
|
||||
|
||||
if action == "Install":
|
||||
apps = inquirer.checkbox(
|
||||
message="Select apps",
|
||||
instruction="([\u2191\u2193]: Select Item. [Space]: Toggle Choice), [Enter]: Confirm",
|
||||
validate=lambda result: len(result) >= 1,
|
||||
invalid_message="should be at least 1 selection",
|
||||
choices=install_choices
|
||||
).execute()
|
||||
microg_variants = ["Standard", "NoGoolag",
|
||||
"UNLP", "Minimal", "MinimalIAP"]
|
||||
if "microg" in apps:
|
||||
microg_variant = inquirer.select(
|
||||
message="Select MicroG variant",
|
||||
choices=microg_variants,
|
||||
default="Standard",
|
||||
).execute()
|
||||
args.microg_variant = microg_variant
|
||||
if "device_spoof" in apps:
|
||||
spoof_profile = inquirer.select(
|
||||
message="Select device profile to spoof",
|
||||
choices=list(PROFILES.keys()),
|
||||
default="pixel6",
|
||||
).execute()
|
||||
args.spoof_profile = spoof_profile
|
||||
args.app = apps
|
||||
install_app(args)
|
||||
elif action == "Remove":
|
||||
apps = inquirer.checkbox(
|
||||
message="Select apps",
|
||||
instruction="([\u2191\u2193]: Select Item. [Space]: Toggle Choice), [Enter]: Confirm",
|
||||
validate=lambda result: len(result) >= 1,
|
||||
invalid_message="should be at least 1 selection",
|
||||
choices=[*install_choices, *hack_choices]
|
||||
).execute()
|
||||
args.app = apps
|
||||
args.microg_variant="Standard"
|
||||
remove_app(args)
|
||||
elif action == "Hack":
|
||||
apps = inquirer.checkbox(
|
||||
message="Select hack options",
|
||||
instruction="([\u2191\u2193]: Select Item. [Space]: Toggle Choice), [Enter]: Confirm",
|
||||
validate=lambda result: len(result) >= 1,
|
||||
invalid_message="should be at least 1 selection",
|
||||
choices=hack_choices
|
||||
).execute()
|
||||
args.option_name = apps
|
||||
hack_option(args)
|
||||
elif action == "Get Google Device ID to Get Certified":
|
||||
AndroidId().get_id()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='''
|
||||
Does stuff like installing Gapps, installing Magisk, installing NDK Translation and getting Android ID for device registration.
|
||||
Use -h flag for help!''')
|
||||
|
||||
subparsers = parser.add_subparsers(title="coomand", dest='command')
|
||||
parser.add_argument('-a', '--android-version',
|
||||
dest='android_version',
|
||||
help='Specify the Android version',
|
||||
default="13",
|
||||
choices=["11", "13"])
|
||||
|
||||
# android command
|
||||
certified = subparsers.add_parser(
|
||||
'certified', help='Get device ID to obtain Play Store certification')
|
||||
certified.set_defaults(func=get_certified)
|
||||
|
||||
install_choices = ["gapps", "microg", "libndk", "libhoudini",
|
||||
"magisk", "mitm", "smartdock", "widevine", "device_spoof"]
|
||||
hack_choices = ["nodataperm", "hidestatusbar"]
|
||||
micrg_variants = ["Standard", "NoGoolag", "UNLP", "Minimal", "MinimalIAP"]
|
||||
remove_choices = install_choices
|
||||
|
||||
arg_template = {
|
||||
"dest": "app",
|
||||
"type": str,
|
||||
"nargs": '+',
|
||||
# "metavar":"",
|
||||
}
|
||||
|
||||
install_help = """
|
||||
gapps: Install Open GApps (Android 11) or MindTheGapps (Android 13)
|
||||
microg: Add microG, Aurora Store and Aurora Droid to WayDriod
|
||||
libndk: Add libndk arm translation, better for AMD CPUs
|
||||
libhoudini: Add libhoudini arm translation, better for Intel CPUs
|
||||
magisk: Install Magisk Delta to WayDroid
|
||||
mitm -c CA_CERT_FILE: Install root CA cert into system trust store
|
||||
smartdock: A desktop mode launcher for Android
|
||||
widevine: Add support for widevine DRM L3
|
||||
"""
|
||||
# install and its aliases
|
||||
install_parser = subparsers.add_parser(
|
||||
'install', formatter_class=argparse.RawTextHelpFormatter, help='Install an app')
|
||||
install_parser.add_argument(
|
||||
**arg_template, choices=install_choices, help=install_help)
|
||||
install_parser.add_argument('-c', '--ca-cert',
|
||||
dest='ca_cert_file',
|
||||
help='[for mitm only] The CA certificate file (*.pem) to install',
|
||||
default=None)
|
||||
install_parser.add_argument('-p', '--spoof-profile',
|
||||
dest='spoof_profile',
|
||||
help='[for device_spoof only] Device profile to spoof',
|
||||
choices=list(PROFILES.keys()),
|
||||
default="pixel6")
|
||||
install_parser.set_defaults(func=install_app)
|
||||
|
||||
# remove and its aliases
|
||||
remove_parser = subparsers.add_parser(
|
||||
'remove', aliases=["uninstall"], help='Remove an app')
|
||||
remove_parser.add_argument(
|
||||
**arg_template, choices=[*remove_choices, * hack_choices], help='Name of app to remove')
|
||||
remove_parser.set_defaults(func=remove_app)
|
||||
|
||||
# hack and its aliases
|
||||
hack_parser = subparsers.add_parser('hack', help='Hack the system')
|
||||
hack_parser.add_argument(
|
||||
'option_name', nargs="+", choices=hack_choices, help='Name of hack option')
|
||||
hack_parser.set_defaults(func=hack_option)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.microg_variant = "Standard"
|
||||
if hasattr(args, 'func'):
|
||||
args_dict = vars(args)
|
||||
helper.check_root()
|
||||
args.func(args)
|
||||
else:
|
||||
helper.check_root()
|
||||
interact()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,32 @@
|
||||
{ lib,
|
||||
stdenvNoCC,
|
||||
lzip,
|
||||
python3,
|
||||
makeWrapper }:
|
||||
let
|
||||
wrappedPath = lib.makeBinPath [ lzip ];
|
||||
in stdenvNoCC.mkDerivation {
|
||||
name = "waydroid_script";
|
||||
|
||||
buildInputs = [
|
||||
(python3.withPackages(ps: with ps; [ tqdm requests inquirerpy ]))
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
makeWrapper
|
||||
];
|
||||
|
||||
src = ./.;
|
||||
|
||||
postPatch = ''
|
||||
patchShebangs main.py
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/libexec
|
||||
cp -r . $out/libexec/waydroid_script
|
||||
mkdir -p $out/bin
|
||||
makeShellWrapper $out/libexec/waydroid_script/main.py $out/bin/waydroid_script \
|
||||
--prefix PATH : "${wrappedPath}"
|
||||
'';
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
tqdm
|
||||
requests
|
||||
InquirerPy
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
from tools import container
|
||||
from tools.helper import shell
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
class AndroidId:
|
||||
def get_id(self):
|
||||
if container.is_running():
|
||||
try:
|
||||
queryout = shell(
|
||||
arg="sqlite3 /data/data/com.google.android.gsf/databases/gservices.db \"select * from main where name = 'android_id';\"",
|
||||
env="ANDROID_RUNTIME_ROOT=/apex/com.android.runtime ANDROID_DATA=/data ANDROID_TZDATA_ROOT=/apex/com.android.tzdata ANDROID_I18N_ROOT=/apex/com.android.i18n"
|
||||
)
|
||||
except:
|
||||
return
|
||||
else:
|
||||
Logger.error("Please make sure Waydroid is running and Gapps has been installed!")
|
||||
return
|
||||
print(queryout.replace("android_id|", "").strip())
|
||||
print(" ^----- Open https://google.com/android/uncertified/?pli=1")
|
||||
print(" Login with your google id then submit the form with id shown above")
|
||||
@@ -0,0 +1,71 @@
|
||||
import configparser
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
PROFILES = {
|
||||
"pixel6": {
|
||||
"ro.product.brand": "google",
|
||||
"ro.product.manufacturer": "Google",
|
||||
"ro.product.model": "Pixel 6",
|
||||
"ro.product.name": "oriole",
|
||||
"ro.product.device": "oriole",
|
||||
"ro.product.board": "oriole",
|
||||
"ro.build.product": "oriole",
|
||||
"ro.build.fingerprint": "google/oriole/oriole:13/TQ3A.230901.001/10750268:user/release-keys",
|
||||
"ro.build.description": "oriole-user 13 TQ3A.230901.001 10750268 release-keys",
|
||||
"ro.build.version.release": "13",
|
||||
"ro.build.version.sdk": "33",
|
||||
"ro.build.type": "user",
|
||||
"ro.build.tags": "release-keys",
|
||||
},
|
||||
"samsung_s21": {
|
||||
"ro.product.brand": "samsung",
|
||||
"ro.product.manufacturer": "samsung",
|
||||
"ro.product.model": "SM-G991B",
|
||||
"ro.product.name": "o1sxeea",
|
||||
"ro.product.device": "o1s",
|
||||
"ro.product.board": "lahaina",
|
||||
"ro.build.product": "o1s",
|
||||
"ro.build.fingerprint": "samsung/o1sxeea/o1s:13/TP1A.220624.014/G991BXXU5EWCA:user/release-keys",
|
||||
"ro.build.description": "o1sxeea-user 13 TP1A.220624.014 G991BXXU5EWCA release-keys",
|
||||
"ro.build.version.release": "13",
|
||||
"ro.build.version.sdk": "33",
|
||||
"ro.build.type": "user",
|
||||
"ro.build.tags": "release-keys",
|
||||
},
|
||||
}
|
||||
|
||||
PROP_KEYS = list(next(iter(PROFILES.values())).keys())
|
||||
|
||||
|
||||
class DeviceSpoof:
|
||||
id = "device_spoof"
|
||||
|
||||
def __init__(self, profile_name="pixel6") -> None:
|
||||
if profile_name not in PROFILES:
|
||||
raise ValueError(f"Unknown profile '{profile_name}'. Choose from: {list(PROFILES.keys())}")
|
||||
self.profile_name = profile_name
|
||||
self.apply_props = PROFILES[profile_name]
|
||||
|
||||
def _read_cfg(self):
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read("/var/lib/waydroid/waydroid.cfg")
|
||||
if not cfg.has_section("properties"):
|
||||
cfg.add_section("properties")
|
||||
return cfg
|
||||
|
||||
def install(self):
|
||||
cfg = self._read_cfg()
|
||||
for key, value in self.apply_props.items():
|
||||
cfg.set("properties", key, value)
|
||||
with open("/var/lib/waydroid/waydroid.cfg", "w") as f:
|
||||
cfg.write(f)
|
||||
Logger.info(f"Device spoofed as '{self.profile_name}'. Restart Waydroid to apply.")
|
||||
|
||||
def uninstall(self):
|
||||
cfg = self._read_cfg()
|
||||
for key in PROP_KEYS:
|
||||
cfg.remove_option("properties", key)
|
||||
with open("/var/lib/waydroid/waydroid.cfg", "w") as f:
|
||||
cfg.write(f)
|
||||
Logger.info("Device spoof properties removed. Restart Waydroid to apply.")
|
||||
@@ -0,0 +1,42 @@
|
||||
import os
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
|
||||
|
||||
class FDroidPriv(General):
|
||||
id = "fdroid priv"
|
||||
dl_links = {"11": ["https://f-droid.org/repo/org.fdroid.fdroid.privileged.ota_2130.zip",
|
||||
"6242cab56d197d80c598593a46da62e4"],
|
||||
"13": ["https://f-droid.org/repo/org.fdroid.fdroid.privileged.ota_2130.zip",
|
||||
"6242cab56d197d80c598593a46da62e4"]}
|
||||
partition = "system"
|
||||
dl_file_name = "org.fdroid.fdroid.privileged.ota_2130.zip"
|
||||
dl_link = ...
|
||||
act_md5 = ...
|
||||
extract_to = "/tmp/fdroid_ota_2130"
|
||||
files = [
|
||||
"app/F-Droid.apk"
|
||||
"app/F-Droid/F-Droid.apk"
|
||||
"etc/permissions/permissions_org.fdroid.fdroid.privileged.xml"
|
||||
"priv-app/F-DroidPrivilegedExtension.apk"
|
||||
"priv-app/F-DroidPrivilegedExtension/F-DroidPrivilegedExtension.apk"
|
||||
]
|
||||
file_map = {
|
||||
"permissions_org.fdroid.fdroid.privileged.xml": "etc/permissions/permissions_org.fdroid.fdroid.privileged.xml",
|
||||
"F-Droid.apk": "app/F-Droid/F-Droid.apk",
|
||||
"F-DroidPrivilegedExtension.apk": "priv-app/F-DroidPrivilegedExtension/F-DroidPrivilegedExtension.apk",
|
||||
}
|
||||
def __init__(self, android_version="11") -> None:
|
||||
super().__init__()
|
||||
self.dl_link = self.dl_links[android_version][0]
|
||||
self.act_md5 = self.dl_links[android_version][1]
|
||||
|
||||
def copy(self):
|
||||
for f, d in self.file_map.items():
|
||||
rro_file = os.path.join(
|
||||
self.copy_dir, self.partition, d)
|
||||
rro_dir = os.path.dirname(rro_file)
|
||||
if not os.path.exists(rro_dir):
|
||||
os.makedirs(rro_dir)
|
||||
shutil.copyfile(os.path.join(self.extract_to, f), rro_file)
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import os
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
from tools.helper import run
|
||||
|
||||
|
||||
class Gapps(General):
|
||||
id = ...
|
||||
partition = "system"
|
||||
dl_links = {
|
||||
"11": {
|
||||
"x86_64": ["https://sourceforge.net/projects/opengapps/files/x86_64/20220503/open_gapps-x86_64-11.0-pico-20220503.zip", "5a6d242be34ad1acf92899c7732afa1b"],
|
||||
"x86": ["https://sourceforge.net/projects/opengapps/files/x86/20220503/open_gapps-x86-11.0-pico-20220503.zip", "efda4943076016d00b40e0874b12ddd3"],
|
||||
"arm64-v8a": ["https://sourceforge.net/projects/opengapps/files/arm64/20220503/open_gapps-arm64-11.0-pico-20220503.zip", "7790055d34bbfc6fe610b0cd263a7add"],
|
||||
"armeabi-v7a": ["https://sourceforge.net/projects/opengapps/files/arm/20220215/open_gapps-arm-11.0-pico-20220215.zip", "8719519fa32ae83a62621c6056d32814"]
|
||||
},
|
||||
"13": {
|
||||
"x86_64": ["https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20231028/MindTheGapps-13.0.0-x86_64-20231028.zip", "63ccebbf93d45c384f58d7c40049d398"],
|
||||
"x86": ["https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20231028/MindTheGapps-13.0.0-x86-20231028.zip", "f12b6a8ed14eedbb4b5b3c932a865956"],
|
||||
"arm64-v8a": ["https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20231028/MindTheGapps-13.0.0-arm64-20231028.zip", "11180da0a5d9f2ed2863882c30a8d556"],
|
||||
"armeabi-v7a": ["https://github.com/s1204IT/MindTheGappsBuilder/releases/download/20231028/MindTheGapps-13.0.0-arm-20231028.zip", "d525c980bac427844aa4cb01628f8a8f"]
|
||||
}
|
||||
}
|
||||
android_version = ...
|
||||
dl_link = ...
|
||||
act_md5 = ...
|
||||
dl_file_name = "gapps.zip"
|
||||
extract_to = "/tmp/gapps/extract"
|
||||
non_apks = [
|
||||
"defaultetc-common.tar.lz",
|
||||
"defaultframework-common.tar.lz",
|
||||
"googlepixelconfig-common.tar.lz",
|
||||
"vending-common.tar.lz"
|
||||
]
|
||||
skip = [
|
||||
"setupwizarddefault-x86_64.tar.lz",
|
||||
"setupwizardtablet-x86_64.tar.lz"
|
||||
]
|
||||
files = [
|
||||
"etc/default-permissions/default-permissions.xml",
|
||||
"etc/default-permissions/opengapps-permissions-q.xml",
|
||||
"etc/permissions/com.google.android.maps.xml",
|
||||
"etc/permissions/com.google.android.media.effects.xml",
|
||||
"etc/permissions/privapp-permissions-google.xml",
|
||||
"etc/permissions/split-permissions-google.xml",
|
||||
"etc/preferred-apps/google.xml",
|
||||
"etc/sysconfig/google.xml",
|
||||
"etc/sysconfig/google_build.xml",
|
||||
"etc/sysconfig/google_exclusives_enable.xml",
|
||||
"etc/sysconfig/google-hiddenapi-package-whitelist.xml",
|
||||
"framework/com.google.android.maps.jar",
|
||||
"framework/com.google.android.media.effects.jar",
|
||||
"priv-app/AndroidMigratePrebuilt",
|
||||
"priv-app/GoogleExtServices",
|
||||
"priv-app/GoogleRestore",
|
||||
"priv-app/CarrierSetup",
|
||||
"priv-app/GoogleExtShared",
|
||||
"priv-app/GoogleServicesFramework",
|
||||
"priv-app/ConfigUpdater",
|
||||
"priv-app/GoogleFeedback",
|
||||
"priv-app/Phonesky",
|
||||
"priv-app/GoogleBackupTransport",
|
||||
"priv-app/GoogleOneTimeInitializer",
|
||||
"priv-app/PrebuiltGmsCore",
|
||||
"priv-app/GoogleContactsSyncAdapter",
|
||||
"priv-app/GooglePartnerSetup",
|
||||
"product/overlay/PlayStoreOverlay.apk",
|
||||
"product/overlay/GmsOverlay.apk.apk",
|
||||
"product/overlay/GmsSettingsProviderOverlay.apk",
|
||||
"system_ext/etc/permissions/privapp-permissions-google-system-ext.xml",
|
||||
"system_ext/priv-app/GoogleFeedback",
|
||||
"system_ext/priv-app/GoogleServicesFramework",
|
||||
"system_ext/priv-app/SetupWizard",
|
||||
"product/priv-app/GmsCore",
|
||||
"product/priv-app/AndroidAutoStub",
|
||||
"product/priv-app/GoogleRestore",
|
||||
"product/priv-app/Phonesky",
|
||||
"product/priv-app/Velvet",
|
||||
"product/priv-app/GooglePartnerSetup",
|
||||
"product/app/GoogleCalendarSyncAdapter",
|
||||
"product/app/PrebuiltExchange3Google",
|
||||
"product/app/GoogleContactsSyncAdapter",
|
||||
"product/framework/com.google.android.dialer.support.jar",
|
||||
"product/lib64/libjni_latinimegoogle.so",
|
||||
"product/etc/default-permissions/default-permissions-google.xml",
|
||||
"product/etc/default-permissions/default-permissions-mtg.xml",
|
||||
"product/etc/sysconfig/google.xml",
|
||||
"product/etc/sysconfig/d2d_cable_migration_feature.xml",
|
||||
"product/etc/sysconfig/google-hiddenapi-package-allowlist.xml",
|
||||
"product/etc/sysconfig/google_build.xml",
|
||||
"product/etc/permissions/privapp-permissions-google-product.xml",
|
||||
"product/etc/permissions/com.google.android.dialer.support.xml",
|
||||
"product/etc/security/fsverity/gms_fsverity_cert.der",
|
||||
"product/lib/libjni_latinimegoogle.so",
|
||||
]
|
||||
|
||||
def __init__(self, android_version="11") -> None:
|
||||
super().__init__()
|
||||
self.android_version = android_version
|
||||
self.dl_link = self.dl_links[android_version][self.arch[0]][0]
|
||||
self.act_md5 = self.dl_links[android_version][self.arch[0]][1]
|
||||
if android_version == "11":
|
||||
self.id = "OpenGapps"
|
||||
else:
|
||||
self.id = "MindTheGapps"
|
||||
|
||||
def copy(self):
|
||||
if self.android_version == "11":
|
||||
return self.copy_11()
|
||||
elif self.android_version == "13":
|
||||
return self.copy_13()
|
||||
|
||||
def copy_11(self):
|
||||
if not os.path.exists(self.extract_to):
|
||||
os.makedirs(self.extract_to)
|
||||
if not os.path.exists(os.path.join(self.extract_to, "appunpack")):
|
||||
os.makedirs(os.path.join(self.extract_to, "appunpack"))
|
||||
|
||||
for lz_file in os.listdir(os.path.join(self.extract_to, "Core")):
|
||||
for d in os.listdir(os.path.join(self.extract_to, "appunpack")):
|
||||
shutil.rmtree(os.path.join(self.extract_to, "appunpack", d))
|
||||
if lz_file not in self.skip:
|
||||
if lz_file not in self.non_apks:
|
||||
print(" Processing app package : " +
|
||||
os.path.join(self.extract_to, "Core", lz_file))
|
||||
run(["tar", "--lzip", "-xvf", os.path.join(self.extract_to, "Core",
|
||||
lz_file), "-C", os.path.join(self.extract_to, "appunpack")])
|
||||
app_name = os.listdir(os.path.join(
|
||||
self.extract_to, "appunpack"))[0]
|
||||
xx_dpi = os.listdir(os.path.join(
|
||||
self.extract_to, "appunpack", app_name))[0]
|
||||
app_priv = os.listdir(os.path.join(
|
||||
self.extract_to, "appunpack", app_name, "nodpi"))[0]
|
||||
app_src_dir = os.path.join(
|
||||
self.extract_to, "appunpack", app_name, xx_dpi, app_priv)
|
||||
for app in os.listdir(app_src_dir):
|
||||
shutil.copytree(os.path.join(app_src_dir, app), os.path.join(
|
||||
self.copy_dir, self.partition, "priv-app", app), dirs_exist_ok=True)
|
||||
for f in os.listdir(os.path.join(self.copy_dir, self.partition, "priv-app", app)):
|
||||
dst_file_path = os.path.join(os.path.join(
|
||||
self.copy_dir, self.partition, "priv-app", app), f)
|
||||
if os.path.splitext(dst_file_path)[1].lower() == ".apk":
|
||||
self.extract_app_lib(dst_file_path)
|
||||
else:
|
||||
print(" Processing extra package : " +
|
||||
os.path.join(self.extract_to, "Core", lz_file))
|
||||
run(["tar", "--lzip", "-xvf", os.path.join(self.extract_to, "Core",
|
||||
lz_file), "-C", os.path.join(self.extract_to, "appunpack")])
|
||||
app_name = os.listdir(os.path.join(
|
||||
self.extract_to, "appunpack"))[0]
|
||||
common_content_dirs = os.listdir(os.path.join(
|
||||
self.extract_to, "appunpack", app_name, "common"))
|
||||
for ccdir in common_content_dirs:
|
||||
shutil.copytree(os.path.join(self.extract_to, "appunpack", app_name, "common", ccdir), os.path.join(
|
||||
self.copy_dir, self.partition, ccdir), dirs_exist_ok=True)
|
||||
|
||||
def copy_13(self):
|
||||
src_dir = os.path.join(self.extract_to, "system")
|
||||
dst_dir = os.path.join(self.copy_dir, self.partition)
|
||||
for root, dirs, files in os.walk(src_dir):
|
||||
dir_name = os.path.basename(root)
|
||||
# 遍历文件
|
||||
for file in files:
|
||||
src_file_path = os.path.join(root, file)
|
||||
dst_file_path = os.path.join(dst_dir, os.path.relpath(
|
||||
src_file_path, src_dir))
|
||||
if not os.path.exists(os.path.dirname(dst_file_path)):
|
||||
os.makedirs(os.path.dirname(dst_file_path))
|
||||
# Logger.info(f"{src_file_path} -> {dst_file_path}")
|
||||
shutil.copy2(src_file_path, dst_file_path)
|
||||
if os.path.splitext(dst_file_path)[1].lower() == ".apk":
|
||||
self.extract_app_lib(dst_file_path)
|
||||
@@ -0,0 +1,190 @@
|
||||
import configparser
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
import hashlib
|
||||
from tools.helper import download_file, get_download_dir, host
|
||||
from tools import container
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
class General:
|
||||
files = []
|
||||
|
||||
@property
|
||||
def arch(self):
|
||||
return host()
|
||||
|
||||
@property
|
||||
def skip_extract(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def download_loc(self):
|
||||
return os.path.join(get_download_dir(), self.dl_file_name)
|
||||
|
||||
@property
|
||||
def copy_dir(self):
|
||||
if container.use_overlayfs():
|
||||
return "/var/lib/waydroid/overlay"
|
||||
else:
|
||||
return "/tmp/waydroid"
|
||||
|
||||
def download(self):
|
||||
Logger.info("Downloading {} now to {} .....".format(
|
||||
self.dl_file_name, self.download_loc))
|
||||
loc_md5 = ""
|
||||
if os.path.isfile(self.download_loc):
|
||||
with open(self.download_loc, "rb") as f:
|
||||
bytes = f.read()
|
||||
loc_md5 = hashlib.md5(bytes).hexdigest()
|
||||
while not os.path.isfile(self.download_loc) or loc_md5 != self.act_md5:
|
||||
if os.path.isfile(self.download_loc):
|
||||
os.remove(self.download_loc)
|
||||
Logger.warning(
|
||||
"md5 mismatches, redownloading now ....")
|
||||
loc_md5 = download_file(self.dl_link, self.download_loc)
|
||||
|
||||
def remove(self):
|
||||
for f in self.files:
|
||||
file = os.path.join(self.copy_dir, self.partition, f)
|
||||
if "*" in file:
|
||||
for wildcard_file in glob.glob(file):
|
||||
if os.path.isdir(wildcard_file):
|
||||
shutil.rmtree(wildcard_file)
|
||||
elif os.path.isfile(wildcard_file):
|
||||
os.remove(wildcard_file)
|
||||
else:
|
||||
if os.path.isdir(file):
|
||||
shutil.rmtree(file)
|
||||
elif os.path.isfile(file):
|
||||
os.remove(file)
|
||||
|
||||
def extract(self):
|
||||
Logger.info(f"Extracting {self.download_loc} to {self.extract_to}")
|
||||
with zipfile.ZipFile(self.download_loc) as z:
|
||||
z.extractall(self.extract_to)
|
||||
|
||||
def add_props(self):
|
||||
bin_dir = os.path.join(self.copy_dir, "system", "etc")
|
||||
resetprop_rc = os.path.join(
|
||||
self.copy_dir, "system/etc/init/resetprop.rc")
|
||||
if not os.path.isfile(os.path.join(bin_dir, "resetprop")):
|
||||
if not os.path.exists(bin_dir):
|
||||
os.makedirs(bin_dir)
|
||||
shutil.copy(os.path.join(os.path.join(os.path.dirname(__file__), "..", "bin",
|
||||
self.arch[0], "resetprop")), bin_dir)
|
||||
os.chmod(os.path.join(bin_dir, "resetprop"), 0o755)
|
||||
if not os.path.isfile(os.path.join(bin_dir, "resetprop.sh")):
|
||||
with open(os.path.join(bin_dir, "resetprop.sh"), "w") as f:
|
||||
f.write("#!/system/bin/sh\n")
|
||||
f.write(
|
||||
"while read line; do /system/etc/resetprop ${line%=*} ${line#*=}; done < /vendor/waydroid.prop\n")
|
||||
os.chmod(os.path.join(bin_dir, "resetprop.sh"), 0o755)
|
||||
if not os.path.isfile(resetprop_rc):
|
||||
if not os.path.exists(os.path.dirname(resetprop_rc)):
|
||||
os.makedirs(os.path.dirname(resetprop_rc))
|
||||
with open(resetprop_rc, "w") as f:
|
||||
f.write(
|
||||
"on post-fs-data\n exec u:r:su:s0 root root -- /system/bin/sh /system/bin/resetprop.sh")
|
||||
os.chmod(resetprop_rc, 0o644)
|
||||
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read("/var/lib/waydroid/waydroid.cfg")
|
||||
|
||||
for key in self.apply_props.keys():
|
||||
if self.apply_props[key]:
|
||||
cfg.set('properties', key, self.apply_props[key])
|
||||
|
||||
with open("/var/lib/waydroid/waydroid.cfg", "w") as f:
|
||||
cfg.write(f)
|
||||
|
||||
def extract_app_lib(self, apk_file_path):
|
||||
lib_dest_dir = os.path.dirname(apk_file_path)
|
||||
with zipfile.ZipFile(apk_file_path, "r") as apk:
|
||||
for file_info in apk.infolist():
|
||||
file_name = file_info.filename
|
||||
file_path = os.path.join(lib_dest_dir, file_name)
|
||||
if file_info.filename.startswith(f"lib/{self.arch[0]}/") and file_name.endswith(".so"):
|
||||
os.makedirs(os.path.dirname(
|
||||
file_path), exist_ok=True)
|
||||
with apk.open(file_info.filename) as src_file, open(file_path, "wb") as dest_file:
|
||||
# Logger.info(f"{src_file} -> {dest_file}")
|
||||
shutil.copyfileobj(src_file, dest_file)
|
||||
|
||||
def set_path_perm(self, path):
|
||||
if "bin" in path.split("/"):
|
||||
perms = [0, 2000, 0o755, 0o777]
|
||||
else:
|
||||
perms = [0, 0, 0o755, 0o644]
|
||||
|
||||
mode = os.stat(path).st_mode
|
||||
|
||||
if os.path.isdir(path):
|
||||
mode |= perms[2]
|
||||
else:
|
||||
mode |= perms[3]
|
||||
|
||||
os.chown(path, perms[0], perms[1])
|
||||
os.chmod(path, mode)
|
||||
|
||||
def set_perm2(self, path, recursive=False):
|
||||
if not os.path.exists(path):
|
||||
return
|
||||
|
||||
if recursive and os.path.isdir(path):
|
||||
for root, dirs, files in os.walk(path):
|
||||
for dir in dirs:
|
||||
self.set_path_perm(os.path.join(root, dir))
|
||||
for file in files:
|
||||
self.set_path_perm(os.path.join(root, file))
|
||||
else:
|
||||
self.set_path_perm(path)
|
||||
|
||||
def set_perm(self):
|
||||
for f in self.files:
|
||||
path = os.path.join(self.copy_dir, self.partition, f)
|
||||
if "*" in path:
|
||||
for wildcard_file in glob.glob(path):
|
||||
self.set_perm2(wildcard_file, recursive=True)
|
||||
else:
|
||||
self.set_perm2(path, recursive=True)
|
||||
|
||||
def remove_props(self):
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read("/var/lib/waydroid/waydroid.cfg")
|
||||
|
||||
for key in self.apply_props.keys():
|
||||
cfg.remove_option('properties', key)
|
||||
|
||||
with open("/var/lib/waydroid/waydroid.cfg", "w") as f:
|
||||
cfg.write(f)
|
||||
|
||||
def copy(self):
|
||||
pass
|
||||
|
||||
def extra1(self):
|
||||
pass
|
||||
|
||||
def extra2(self):
|
||||
pass
|
||||
|
||||
def install(self):
|
||||
self.download()
|
||||
if not self.skip_extract:
|
||||
self.extract()
|
||||
self.copy()
|
||||
self.extra1()
|
||||
if hasattr(self, "apply_props"):
|
||||
self.add_props()
|
||||
self.set_perm()
|
||||
Logger.info(f"{self.id} installation finished")
|
||||
|
||||
def uninstall(self):
|
||||
self.remove()
|
||||
if hasattr(self, "apply_props"):
|
||||
self.remove_props()
|
||||
self.extra2()
|
||||
self.set_perm()
|
||||
Logger.info("Uninstallation finished")
|
||||
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
|
||||
|
||||
class HideStatusBar(General):
|
||||
id = "hide status bar"
|
||||
dl_links = {"11": ["https://github.com/ayasa520/hide-status-bar/releases/download/v0.0.2/app-release.apk",
|
||||
"ff2fe63ddfb4b035e6720a1b195b2355"]}
|
||||
partition = "system"
|
||||
dl_file_name = "hidestatusbar.apk"
|
||||
dl_link = ...
|
||||
act_md5 = ...
|
||||
files = [
|
||||
"product/overlay/"+dl_file_name
|
||||
]
|
||||
def __init__(self, android_version="11") -> None:
|
||||
super().__init__()
|
||||
self.dl_link = self.dl_links[android_version][0]
|
||||
self.act_md5 = self.dl_links[android_version][1]
|
||||
|
||||
def copy(self):
|
||||
rro_dir = os.path.join(
|
||||
self.copy_dir, self.partition, "product", "overlay")
|
||||
if not os.path.exists(rro_dir):
|
||||
os.makedirs(rro_dir)
|
||||
shutil.copyfile(self.download_loc, os.path.join(
|
||||
rro_dir, "hidestatusbar.apk"))
|
||||
|
||||
def skip_extract(self):
|
||||
return True
|
||||
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
class Houdini(General):
|
||||
id = "libhoudini"
|
||||
partition = "system"
|
||||
dl_links = {
|
||||
"11": ["https://github.com/supremegamers/vendor_intel_proprietary_houdini/archive/81f2a51ef539a35aead396ab7fce2adf89f46e88.zip", "fbff756612b4144797fbc99eadcb6653"],
|
||||
"13": ["https://github.com/supremegamers/vendor_intel_proprietary_houdini/archive/2f8f088671182e17e67321e098e8411a3972a628.zip", "37fe0899f1e4da7f9a724cca1c1ab1ea"]
|
||||
}
|
||||
act_md5 = ...
|
||||
dl_link = ...
|
||||
dl_file_name = "libhoudini.zip"
|
||||
extract_to = "/tmp/houdiniunpack"
|
||||
init_rc_component = """
|
||||
on early-init
|
||||
mount binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
|
||||
|
||||
on property:ro.enable.native.bridge.exec=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"
|
||||
"""
|
||||
apply_props = {
|
||||
"ro.product.cpu.abilist": "x86_64,x86,arm64-v8a,armeabi-v7a,armeabi",
|
||||
"ro.product.cpu.abilist32": "x86,armeabi-v7a,armeabi",
|
||||
"ro.product.cpu.abilist64": "x86_64,arm64-v8a",
|
||||
"ro.dalvik.vm.native.bridge": "libhoudini.so",
|
||||
"ro.enable.native.bridge.exec": "1",
|
||||
"ro.dalvik.vm.isa.arm": "x86",
|
||||
"ro.dalvik.vm.isa.arm64": "x86_64"
|
||||
}
|
||||
files = [
|
||||
"bin/arm",
|
||||
"bin/arm64",
|
||||
"bin/houdini",
|
||||
"bin/houdini64",
|
||||
"etc/binfmt_misc",
|
||||
"etc/init/houdini.rc",
|
||||
"lib/arm",
|
||||
"lib/libhoudini.so",
|
||||
"lib64/arm64",
|
||||
"lib64/libhoudini.so"
|
||||
]
|
||||
|
||||
def __init__(self, android_version="11") -> None:
|
||||
super().__init__()
|
||||
self.dl_link = self.dl_links[android_version][0]
|
||||
self.act_md5 = self.dl_links[android_version][1]
|
||||
|
||||
def copy(self):
|
||||
Logger.info("Copying libhoudini library files ...")
|
||||
name = re.findall("([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, self.partition), dirs_exist_ok=True)
|
||||
init_path = os.path.join(
|
||||
self.copy_dir, self.partition, "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)
|
||||
@@ -0,0 +1,184 @@
|
||||
import gzip
|
||||
import os
|
||||
import shutil
|
||||
import re
|
||||
from stuff.general import General
|
||||
from tools.helper import download_file, get_data_dir, host
|
||||
from tools.logger import Logger
|
||||
from tools import container
|
||||
|
||||
class Magisk(General):
|
||||
id = "magisk delta"
|
||||
partition = "system"
|
||||
dl_link = "https://github.com/mistrmochov/magiskdeltaorig/raw/main/app-release.apk"
|
||||
dl_file_name = "magisk.apk"
|
||||
extract_to = "/tmp/magisk_unpack"
|
||||
magisk_dir = os.path.join(partition, "etc", "init", "magisk")
|
||||
files = ["etc/init/magisk", "etc/init/bootanim.rc"]
|
||||
oringinal_bootanim = """
|
||||
service bootanim /system/bin/bootanimation
|
||||
class core animation
|
||||
user graphics
|
||||
group graphics audio
|
||||
disabled
|
||||
oneshot
|
||||
ioprio rt 0
|
||||
task_profiles MaxPerformance
|
||||
|
||||
"""
|
||||
bootanim_component = f"""
|
||||
on post-fs-data
|
||||
start logd
|
||||
exec u:r:su:s0 root root -- /system/etc/init/magisk/magiskpolicy --live --magisk
|
||||
exec u:r:magisk:s0 root root -- /system/etc/init/magisk/magiskpolicy --live --magisk
|
||||
exec u:r:update_engine:s0 root root -- /system/etc/init/magisk/magiskpolicy --live --magisk
|
||||
mkdir /dev/magisk_iqeoVo2mDrO 700
|
||||
exec u:r:su:s0 root root -- /system/etc/init/magisk/magisk64 --auto-selinux --setup-sbin /system/etc/init/magisk /dev/magisk_iqeoVo2mDrO
|
||||
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --post-fs-data
|
||||
|
||||
on nonencrypted
|
||||
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --service
|
||||
|
||||
on property:vold.decrypt=trigger_restart_framework
|
||||
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --service
|
||||
|
||||
on property:sys.boot_completed=1
|
||||
mkdir /data/adb/magisk 755
|
||||
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --boot-complete
|
||||
|
||||
on property:init.svc.zygote=restarting
|
||||
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --zygote-restart
|
||||
|
||||
on property:init.svc.zygote=stopped
|
||||
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --zygote-restart
|
||||
"""
|
||||
|
||||
def download(self):
|
||||
if os.path.isfile(self.download_loc):
|
||||
os.remove(self.download_loc)
|
||||
Logger.info("Downloading latest Magisk-Delta to {} now ...".format(self.download_loc))
|
||||
download_file(self.dl_link, self.download_loc)
|
||||
|
||||
# require additional setup
|
||||
def setup(self):
|
||||
Logger.info("Additional setup")
|
||||
magisk_absolute_dir = os.path.join(self.copy_dir, self.magisk_dir)
|
||||
data_dir = get_data_dir()
|
||||
data_magisk_dir = os.path.join(data_dir, "adb", "magisk")
|
||||
shutil.copytree(magisk_absolute_dir, data_magisk_dir, dirs_exist_ok=True)
|
||||
|
||||
# General.install() calls set_perm() only over self.files, which are paths
|
||||
# under copy_dir (the overlay). This /data/adb copy is made here, before
|
||||
# that runs, so it keeps the 0644 the binaries inherited from the apk
|
||||
# extraction. A non-executable /data/adb/magisk/magisk64 makes magisk_env()
|
||||
# abort at post-fs-data: root still works from the overlay copy, but Zygisk
|
||||
# silently never initialises (Magisk reports "Zygisk: No" no matter what
|
||||
# the zygisk setting says). Apply the same perms set_path_perm() gives
|
||||
# magisk paths, to this copy too.
|
||||
self.set_data_magisk_perm(data_magisk_dir)
|
||||
|
||||
def set_data_magisk_perm(self, data_magisk_dir):
|
||||
if not os.path.isdir(data_magisk_dir):
|
||||
return
|
||||
os.chmod(data_magisk_dir, 0o755)
|
||||
for root, dirnames, filenames in os.walk(data_magisk_dir):
|
||||
for dirname in dirnames:
|
||||
os.chmod(os.path.join(root, dirname), 0o755)
|
||||
for filename in filenames:
|
||||
os.chmod(os.path.join(root, filename), 0o755)
|
||||
|
||||
def copy(self):
|
||||
magisk_absolute_dir = os.path.join(self.copy_dir, self.magisk_dir)
|
||||
if not os.path.exists(magisk_absolute_dir):
|
||||
os.makedirs(magisk_absolute_dir, exist_ok=True)
|
||||
|
||||
if not os.path.exists(os.path.join(self.copy_dir, "sbin")):
|
||||
os.makedirs(os.path.join(self.copy_dir, "sbin"), exist_ok=True)
|
||||
|
||||
Logger.info("Copying magisk libs now ...")
|
||||
|
||||
lib_dir = os.path.join(self.extract_to, "lib", self.arch[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('lib(.*)\.so', filename)
|
||||
n_path = os.path.join(magisk_absolute_dir, filename.group(1))
|
||||
shutil.copyfile(o_path, n_path)
|
||||
shutil.copyfile(self.download_loc, os.path.join(magisk_absolute_dir,"magisk.apk") )
|
||||
shutil.copytree(os.path.join(self.extract_to, "assets", "chromeos"), os.path.join(magisk_absolute_dir, "chromeos"), dirs_exist_ok=True)
|
||||
assets_files = [
|
||||
"addon.d.sh",
|
||||
"boot_patch.sh",
|
||||
"stub.apk",
|
||||
"util_functions.sh"
|
||||
]
|
||||
for f in assets_files:
|
||||
shutil.copyfile(os.path.join(self.extract_to, "assets", f), os.path.join(magisk_absolute_dir, f))
|
||||
|
||||
# Updating Magisk from Magisk manager will modify bootanim.rc,
|
||||
# So it is necessary to backup the original bootanim.rc.
|
||||
bootanim_path = os.path.join(self.copy_dir, self.partition, "etc", "init", "bootanim.rc")
|
||||
gz_filename = os.path.join(bootanim_path)+".gz"
|
||||
with gzip.open(gz_filename,'wb') as f_gz:
|
||||
f_gz.write(self.oringinal_bootanim.encode('utf-8'))
|
||||
with open(bootanim_path, "w") as initfile:
|
||||
initfile.write(self.oringinal_bootanim+self.bootanim_component)
|
||||
|
||||
def set_path_perm(self, path):
|
||||
if "magisk" in path.split("/"):
|
||||
perms = [0, 2000, 0o755, 0o755]
|
||||
else:
|
||||
perms = [0, 0, 0o755, 0o644]
|
||||
|
||||
mode = os.stat(path).st_mode
|
||||
|
||||
if os.path.isdir(path):
|
||||
mode |= perms[2]
|
||||
else:
|
||||
mode |= perms[3]
|
||||
|
||||
os.chown(path, perms[0], perms[1])
|
||||
os.chmod(path, mode)
|
||||
|
||||
def extra1(self):
|
||||
self.delete_upper()
|
||||
self.setup()
|
||||
|
||||
# Delete the contents of upperdir
|
||||
def delete_upper(self):
|
||||
if container.use_overlayfs():
|
||||
sys_overlay_rw = "/var/lib/waydroid/overlay_rw"
|
||||
files = [
|
||||
"system/system/etc/init/bootanim.rc",
|
||||
"system/system/etc/init/bootanim.rc.gz",
|
||||
"system/system/etc/init/magisk",
|
||||
"system/system/addon.d/99-magisk.sh",
|
||||
"vendor/etc/selinux/precompiled_sepolicy"
|
||||
]
|
||||
|
||||
for f in files:
|
||||
file = os.path.join(sys_overlay_rw, f)
|
||||
if os.path.isdir(file):
|
||||
shutil.rmtree(file)
|
||||
elif os.path.isfile(file) or os.path.exists(file):
|
||||
os.remove(file)
|
||||
|
||||
def extra2(self):
|
||||
self.delete_upper()
|
||||
data_dir = get_data_dir()
|
||||
files = [
|
||||
os.path.join(data_dir, "adb/magisk.db"),
|
||||
os.path.join(data_dir, "adb/magisk")
|
||||
]
|
||||
for file in files:
|
||||
if os.path.isdir(file):
|
||||
shutil.rmtree(file)
|
||||
elif os.path.isfile(file):
|
||||
os.remove(file)
|
||||
bootanim_path = os.path.join(self.copy_dir, self.partition, "etc", "init", "bootanim.rc")
|
||||
if container.use_overlayfs():
|
||||
if os.path.exists(bootanim_path):
|
||||
os.remove(bootanim_path)
|
||||
else:
|
||||
with open(bootanim_path, "w") as initfile:
|
||||
initfile.write(self.oringinal_bootanim)
|
||||
@@ -0,0 +1,176 @@
|
||||
import os
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
class MicroG(General):
|
||||
id = "MicroG"
|
||||
partition = "system"
|
||||
fdroid_repo_apks = {
|
||||
"com.aurora.store_41.apk": "9e6c79aefde3f0bbfedf671a2d73d1be",
|
||||
"com.etesync.syncadapter_20300.apk": "997d6de7d41c454d39fc22cd7d8fc3c2",
|
||||
"com.aurora.adroid_8.apk": "0010bf93f02c2d18daf9e767035fefc5",
|
||||
"org.fdroid.fdroid.privileged_2130.apk": "b04353155aceb36207a206d6dd14ba6a",
|
||||
"org.microg.nlp.backend.ichnaea_20036.apk": "0b3cb65f8458d1a5802737c7392df903",
|
||||
"org.microg.nlp.backend.nominatim_20042.apk": "88e7397cbb9e5c71c8687d3681a23383",
|
||||
}
|
||||
microg_apks= {
|
||||
"com.google.android.gms-223616054.apk": "a945481ca5d33a03bc0f9418263c3228",
|
||||
"com.google.android.gsf-8.apk": "b2b4ea3642df6158e14689a4b2a246d4",
|
||||
"com.android.vending-22.apk": "6815d191433ffcd8fa65923d5b0b0573",
|
||||
"org.microg.gms.droidguard-14.apk": "4734b41c1a6bc34a541053ddde7a0f8e"
|
||||
}
|
||||
priv_apps = ["com.google.android.gms", "com.android.vending"]
|
||||
dl_links = {
|
||||
"Standard": [
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-Standard-2.11.1-20230429100529.zip",
|
||||
"0fe332a9caa3fbb294f2e2b50f720c6b"
|
||||
],
|
||||
"NoGoolag": [
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-NoGoolag-2.11.1-20230429100545.zip",
|
||||
"ff920f33f4c874eeae4c0444be427c68"
|
||||
],
|
||||
"UNLP": [
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-UNLP-2.11.1-20230429100555.zip",
|
||||
"6136b383153c2a6797d14fb4d7ca3f97"
|
||||
],
|
||||
"Minimal": [
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-Minimal-2.11.1-20230429100521.zip",
|
||||
"afb87eb64e7749cfd72c4760d85849da"
|
||||
],
|
||||
"MinimalIAP": [
|
||||
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-MinimalIAP-2.11.1-20230429100556.zip",
|
||||
"cc071f4f776cbc16c4c1f707aff1f7fa"
|
||||
]
|
||||
}
|
||||
dl_link = ...
|
||||
act_md5 = ...
|
||||
dl_file_name = ...
|
||||
sdk = ...
|
||||
extract_to = "/tmp/microg/extract"
|
||||
rc_content = '''
|
||||
on property:sys.boot_completed=1
|
||||
start microg_service
|
||||
|
||||
service microg_service /system/bin/sh /system/bin/npem
|
||||
user root
|
||||
group root
|
||||
oneshot
|
||||
'''
|
||||
files = [
|
||||
"priv-app/GoogleBackupTransport",
|
||||
"priv-app/MicroGUNLP",
|
||||
"priv-app/MicroGGMSCore",
|
||||
"priv-app/MicroGGMSCore/lib/x86_64/libmapbox-gl.so",
|
||||
"priv-app/MicroGGMSCore/lib/x86_64/libconscrypt_gmscore_jni.so",
|
||||
"priv-app/MicroGGMSCore/lib/x86_64/libcronet.102.0.5005.125.so",
|
||||
"priv-app/PatchPhonesky",
|
||||
"priv-app/PatchPhonesky/lib/x86_64/libempty_x86_64.so",
|
||||
"priv-app/AuroraServices",
|
||||
"bin/npem",
|
||||
"app/GoogleCalendarSyncAdapter",
|
||||
"app/NominatimNLPBackend",
|
||||
"app/MicroGGSFProxy",
|
||||
"app/LocalGSMNLPBackend",
|
||||
"app/DejaVuNLPBackend",
|
||||
"app/MozillaUnifiedNLPBackend",
|
||||
"app/AppleNLPBackend",
|
||||
"app/AuroraDroid",
|
||||
"app/LocalWiFiNLPBackend",
|
||||
"app/GoogleContactsSyncAdapter",
|
||||
"app/MicroGGSFProxy/MicroGGSFProxy",
|
||||
"framework/com.google.widevine.software.drm.jar",
|
||||
"framework/com.google.android.media.effects.jar",
|
||||
"framework/com.google.android.maps.jar",
|
||||
"lib64/libjni_keyboarddecoder.so",
|
||||
"lib64/libjni_latinimegoogle.so",
|
||||
"etc/default-permissions/microg-permissions.xml",
|
||||
"etc/default-permissions/microg-permissions-unlp.xml",
|
||||
"etc/default-permissions/gsync.xml",
|
||||
"etc/sysconfig/nogoolag.xml",
|
||||
"etc/sysconfig/nogoolag-unlp.xml",
|
||||
"etc/init/microg.rc",
|
||||
"etc/permissions/com.google.android.backuptransport.xml",
|
||||
"etc/permissions/com.android.vending.xml",
|
||||
"etc/permissions/foss-permissions.xml",
|
||||
"etc/permissions/com.google.android.gms.xml",
|
||||
"etc/permissions/com.aurora.services.xml",
|
||||
"etc/permissions/com.google.android.maps.xml",
|
||||
"etc/permissions/com.google.widevine.software.drm.xml",
|
||||
"etc/permissions/com.google.android.media.effects.xml",
|
||||
"lib/libjni_keyboarddecoder.so",
|
||||
"lib/libjni_latinimegoogle.so",
|
||||
]
|
||||
|
||||
def __init__(self, android_version="11", variant="Standard") -> None:
|
||||
super().__init__()
|
||||
self.dl_link = self.dl_links[variant][0]
|
||||
self.act_md5 = self.dl_links[variant][1]
|
||||
self.id = self.id+f"-{variant}"
|
||||
self.dl_file_name = f'MinMicroG-{variant}.zip'
|
||||
if android_version == "11":
|
||||
self.sdk = 30
|
||||
elif android_version == "13":
|
||||
self.sdk = 33
|
||||
|
||||
def copy(self):
|
||||
Logger.info("Copying libs and apks...")
|
||||
dst_dir = os.path.join(self.copy_dir, self.partition)
|
||||
src_dir = os.path.join(self.extract_to, "system")
|
||||
if "arm" in self.arch[0]:
|
||||
sub_arch = "arm"
|
||||
else:
|
||||
sub_arch = "x86"
|
||||
if 64 == self.arch[1]:
|
||||
arch = f"{sub_arch}{'' if sub_arch=='arm' else '_'}64"
|
||||
for root, dirs, files in os.walk(src_dir):
|
||||
flag = False
|
||||
dir_name = os.path.basename(root)
|
||||
# 遍历文件
|
||||
if dir_name.startswith('-') and dir_name.endswith('-'):
|
||||
archs, sdks = [], []
|
||||
for i in dir_name.split("-"):
|
||||
if i.isdigit():
|
||||
sdks.append(i)
|
||||
elif i:
|
||||
archs.append(i)
|
||||
if len(archs) != 0 and arch not in archs and sub_arch not in archs or len(sdks) != 0 and str(self.sdk) not in sdks:
|
||||
continue
|
||||
else:
|
||||
flag = True
|
||||
|
||||
for file in files:
|
||||
src_file_path = os.path.join(root, file)
|
||||
if not flag:
|
||||
dst_file_path = os.path.join(dst_dir, os.path.relpath(
|
||||
src_file_path, src_dir))
|
||||
else:
|
||||
dst_file_path = os.path.join(dst_dir, os.path.relpath(
|
||||
os.path.join(os.path.dirname(root), file), src_dir))
|
||||
if not os.path.exists(os.path.dirname(dst_file_path)):
|
||||
os.makedirs(os.path.dirname(dst_file_path))
|
||||
# Logger.info(f"{src_file_path} -> {dst_file_path}")
|
||||
shutil.copy2(src_file_path, dst_file_path)
|
||||
if os.path.splitext(dst_file_path)[1].lower() == ".apk":
|
||||
self.extract_app_lib(dst_file_path)
|
||||
|
||||
rc_dir = os.path.join(dst_dir, "etc/init/microg.rc")
|
||||
if not os.path.exists(os.path.dirname(rc_dir)):
|
||||
os.makedirs(os.path.dirname(rc_dir))
|
||||
with open(rc_dir, "w") as f:
|
||||
f.write(self.rc_content)
|
||||
|
||||
def extra2(self):
|
||||
system_dir = os.path.join(self.copy_dir, self.partition)
|
||||
files = [key.split("_")[0] for key in self.fdroid_repo_apks.keys()]
|
||||
files += [key.split("-")[0] for key in self.microg_apks.keys()]
|
||||
for f in files:
|
||||
if f in self.priv_apps:
|
||||
file = os.path.join(system_dir, "priv-app", f)
|
||||
else:
|
||||
file = os.path.join(system_dir, "app", f)
|
||||
if os.path.isdir(file):
|
||||
shutil.rmtree(file)
|
||||
elif os.path.isfile(file):
|
||||
os.remove(file)
|
||||
@@ -0,0 +1,40 @@
|
||||
import os
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
from tools.helper import run
|
||||
from tools.logger import Logger
|
||||
|
||||
class Mitm(General):
|
||||
id = "mitm"
|
||||
partition = "system"
|
||||
|
||||
def __init__(self, ca_cert_file: str=None) -> None:
|
||||
super().__init__()
|
||||
self.ca_cert_file = ca_cert_file
|
||||
|
||||
def download(self):
|
||||
pass
|
||||
|
||||
def skip_extract(self):
|
||||
return True
|
||||
|
||||
def copy(self):
|
||||
file_hash = run([
|
||||
'openssl', 'x509', '-noout', '-subject_hash_old', '-in',
|
||||
self.ca_cert_file,
|
||||
]).stdout.decode("ascii").strip()
|
||||
target_dir = os.path.join(
|
||||
self.copy_dir, self.partition, "etc", "security", "cacerts")
|
||||
Logger.info(f"Creating directory: {target_dir}")
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
target_path = os.path.join(target_dir, f'{file_hash}.0')
|
||||
Logger.info(f"Copying {self.ca_cert_file} to system trust store")
|
||||
Logger.info(f"Target file: {target_path}")
|
||||
shutil.copyfile(self.ca_cert_file, target_path)
|
||||
os.chmod(target_path, 0o644)
|
||||
|
||||
def install(self):
|
||||
if not self.ca_cert_file:
|
||||
raise ValueError(
|
||||
"This command requires the --ca-cert switch and a *.pem file")
|
||||
super().install()
|
||||
@@ -0,0 +1,52 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
from tools.logger import Logger
|
||||
|
||||
class Ndk(General):
|
||||
id = "libndk"
|
||||
partition = "system"
|
||||
dl_links = {
|
||||
"11": ["https://github.com/supremegamers/vendor_google_proprietary_ndk_translation-prebuilt/archive/9324a8914b649b885dad6f2bfd14a67e5d1520bf.zip", "c9572672d1045594448068079b34c350"],
|
||||
"13": ["https://github.com/supremegamers/vendor_google_proprietary_ndk_translation-prebuilt/archive/68734c52556d3d7a6db34c603dd9276915c29f2f.zip", "0b2207c490fcb400aa5c87fcf0d52d38"]
|
||||
}
|
||||
dl_file_name = "libndktranslation.zip"
|
||||
extract_to = "/tmp/libndkunpack"
|
||||
apply_props = {
|
||||
"ro.product.cpu.abilist": "x86_64,x86,arm64-v8a,armeabi-v7a,armeabi",
|
||||
"ro.product.cpu.abilist32": "x86,armeabi-v7a,armeabi",
|
||||
"ro.product.cpu.abilist64": "x86_64,arm64-v8a",
|
||||
"ro.dalvik.vm.native.bridge": "libndk_translation.so",
|
||||
"ro.enable.native.bridge.exec": "1",
|
||||
"ro.vendor.enable.native.bridge.exec": "1",
|
||||
"ro.vendor.enable.native.bridge.exec64": "1",
|
||||
"ro.ndk_translation.version": "0.2.3",
|
||||
"ro.dalvik.vm.isa.arm": "x86",
|
||||
"ro.dalvik.vm.isa.arm64": "x86_64"
|
||||
}
|
||||
files = [
|
||||
"bin/arm",
|
||||
"bin/arm64",
|
||||
"bin/ndk_translation_program_runner_binfmt_misc",
|
||||
"bin/ndk_translation_program_runner_binfmt_misc_arm64",
|
||||
"etc/binfmt_misc",
|
||||
"etc/ld.config.arm.txt",
|
||||
"etc/ld.config.arm64.txt",
|
||||
"etc/init/ndk_translation.rc",
|
||||
"lib/arm",
|
||||
"lib64/arm64",
|
||||
"lib/libndk*",
|
||||
"lib64/libndk*"
|
||||
]
|
||||
|
||||
def __init__(self, android_version="11") -> None:
|
||||
super().__init__()
|
||||
self.dl_link = self.dl_links[android_version][0]
|
||||
self.act_md5 = self.dl_links[android_version][1]
|
||||
|
||||
def copy(self):
|
||||
Logger.info("Copying libndk library files ...")
|
||||
name = re.findall("([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||
shutil.copytree(os.path.join(self.extract_to, "vendor_google_proprietary_ndk_translation-prebuilt-" + name,
|
||||
"prebuilts"), os.path.join(self.copy_dir, self.partition), dirs_exist_ok=True)
|
||||
@@ -0,0 +1,79 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
from tools.helper import backup, restore
|
||||
from tools.logger import Logger
|
||||
from tools import container
|
||||
|
||||
|
||||
class Nodataperm(General):
|
||||
id = "nodataperm"
|
||||
dl_links = {
|
||||
"11": {
|
||||
"x86_64": [
|
||||
"https://github.com/ayasa520/hack_full_data_permission/archive/d4beab7780eb792059d33e77d865579c9ee41546.zip",
|
||||
"b0e3908ffcf5df8ea62f4929aa680f1a"
|
||||
],
|
||||
},
|
||||
"13": {
|
||||
"x86_64": [
|
||||
"https://github.com/ayasa520/hack_full_data_permission/archive/d4beab7780eb792059d33e77d865579c9ee41546.zip",
|
||||
"b0e3908ffcf5df8ea62f4929aa680f1a"
|
||||
],
|
||||
},
|
||||
}
|
||||
dl_file_name = "nodataperm.zip"
|
||||
extract_to = "/tmp/nodataperm"
|
||||
dl_link = None
|
||||
act_md5 = None
|
||||
partition = "system"
|
||||
files = [
|
||||
"etc/nodataperm.sh",
|
||||
"etc/init/nodataperm.rc",
|
||||
"framework/services.jar",
|
||||
"framework/services.jar.prof",
|
||||
"framework/services.jar.bprof",
|
||||
]
|
||||
|
||||
def __init__(self, android_version="11") -> None:
|
||||
super().__init__()
|
||||
print("ok")
|
||||
arch = self.arch[0]
|
||||
if android_version not in self.dl_links:
|
||||
raise KeyError(f"No download links for Android version '{android_version}'")
|
||||
if arch not in self.dl_links[android_version]:
|
||||
raise KeyError(f"No download links for architecture '{arch}' in Android version '{android_version}'")
|
||||
self.dl_link = self.dl_links[android_version][arch][0]
|
||||
self.act_md5 = self.dl_links[android_version][arch][1]
|
||||
|
||||
def copy(self):
|
||||
name = re.findall(r"([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||
extract_path = os.path.join(
|
||||
self.extract_to, f"hack_full_data_permission-{name}")
|
||||
if not container.use_overlayfs():
|
||||
services_jar = os.path.join(
|
||||
self.copy_dir, self.partition, "framework", "services.jar")
|
||||
services_jar_prof = os.path.join(
|
||||
self.copy_dir, self.partition, "framework", "services.jar.prof")
|
||||
services_jar_bprof = os.path.join(
|
||||
self.copy_dir, self.partition, "framework", "services.jar.bprof")
|
||||
backup(services_jar)
|
||||
backup(services_jar_prof)
|
||||
backup(services_jar_bprof)
|
||||
|
||||
Logger.info(f"Copying {self.id} library files ...")
|
||||
shutil.copytree(extract_path, os.path.join(
|
||||
self.copy_dir, self.partition), dirs_exist_ok=True)
|
||||
|
||||
def extra2(self):
|
||||
if not container.use_overlayfs():
|
||||
services_jar = os.path.join(
|
||||
self.copy_dir, self.partition, "framework", "services.jar")
|
||||
services_jar_prof = os.path.join(
|
||||
self.copy_dir, self.partition, "framework", "services.jar.prof")
|
||||
services_jar_bprof = os.path.join(
|
||||
self.copy_dir, self.partition, "framework", "services.jar.bprof")
|
||||
restore(services_jar)
|
||||
restore(services_jar_prof)
|
||||
restore(services_jar_bprof)
|
||||
@@ -0,0 +1,68 @@
|
||||
import os
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
|
||||
class Smartdock(General):
|
||||
id = "smartdock"
|
||||
dl_link = "https://f-droid.org/repo/cu.axel.smartdock_1130.apk"
|
||||
partition = "system"
|
||||
dl_file_name = "smartdock.apk"
|
||||
act_md5 = "6bfedb959ef5855c3782e8001cb67f86"
|
||||
apply_props = { "qemu.hw.mainkeys" : "1" }
|
||||
skip_extract = True
|
||||
permissions = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<permissions>
|
||||
<privapp-permissions package="cu.axel.smartdock">
|
||||
<permission name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<permission name="android.permission.GET_TASKS"/>
|
||||
<permission name="android.permission.REORDER_TASKS"/>
|
||||
<permission name="android.permission.REMOVE_TASKS" />
|
||||
<permission name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<permission name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<permission name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<permission name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<permission name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<permission name="android.permission.MANAGE_USERS"/>
|
||||
<permission name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<permission name="android.permission.BLUETOOTH_CONNECT"/>
|
||||
<permission name="android.permission.BLUETOOTH"/>
|
||||
<permission name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
||||
<permission name="android.permission.ACCESS_SUPERUSER"/>
|
||||
<permission name="android.permission.PACKAGE_USAGE_STATS" />
|
||||
<permission name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<permission name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
</privapp-permissions>
|
||||
</permissions>
|
||||
"""
|
||||
files = [
|
||||
"etc/permissions/permissions_cu.axel.smartdock.xml",
|
||||
"priv-app/SmartDock",
|
||||
"etc/init/smartdock.rc"
|
||||
]
|
||||
rc_content = '''
|
||||
on property:sys.boot_completed=1
|
||||
start set_home_activity
|
||||
|
||||
service set_home_activity /system/bin/sh -c "cmd package set-home-activity cu.axel.smartdock/.activities.LauncherActivity"
|
||||
user root
|
||||
group root
|
||||
oneshot
|
||||
'''
|
||||
|
||||
def copy(self):
|
||||
if not os.path.exists(os.path.join(self.copy_dir, self.partition, "priv-app", "SmartDock")):
|
||||
os.makedirs(os.path.join(self.copy_dir, self.partition, "priv-app", "SmartDock"))
|
||||
if not os.path.exists(os.path.join(self.copy_dir, self.partition, "etc", "permissions")):
|
||||
os.makedirs(os.path.join(self.copy_dir, self.partition, "etc", "permissions"))
|
||||
shutil.copyfile(os.path.join(self.download_loc),
|
||||
os.path.join(self.copy_dir, self.partition, "priv-app/SmartDock/smartdock.apk"))
|
||||
|
||||
with open(os.path.join(self.copy_dir, self.partition, "etc", "permissions", "permissions_cu.axel.smartdock.xml"), "w") as f:
|
||||
f.write(self.permissions)
|
||||
|
||||
rc_dir = os.path.join(self.copy_dir, self.partition, "etc/init/smartdock.rc")
|
||||
if not os.path.exists(os.path.dirname(rc_dir)):
|
||||
os.makedirs(os.path.dirname(rc_dir))
|
||||
self.extract_app_lib(os.path.join(self.copy_dir, self.partition, "priv-app/SmartDock/smartdock.apk"))
|
||||
with open(rc_dir, "w") as f:
|
||||
f.write(self.rc_content)
|
||||
@@ -0,0 +1,57 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from stuff.general import General
|
||||
from tools.logger import Logger
|
||||
|
||||
|
||||
class Widevine(General):
|
||||
id = "widevine"
|
||||
partition = "vendor"
|
||||
dl_links = {
|
||||
# "x86": ["https://github.com/supremegamers/vendor_google_proprietary_widevine-prebuilt/archive/94c9ee172e3d78fecc81863f50a59e3646f7a2bd.zip", "a31f325453c5d239c21ecab8cfdbd878"],
|
||||
"x86_64": {
|
||||
"11": ["https://github.com/supremegamers/vendor_google_proprietary_widevine-prebuilt/archive/48d1076a570837be6cdce8252d5d143363e37cc1.zip",
|
||||
"f587b8859f9071da4bca6cea1b9bed6a"],
|
||||
"13": ["https://github.com/WayDroid-ATV/vendor_google_proprietary_widevine-prebuilt/archive/679552343d8b2e8d7a19b6df61c7a03963d0c75b.zip",
|
||||
"80ab79ea85c7b2556baedb371a54e01c"]
|
||||
},
|
||||
# "armeabi-v7a": ["https://github.com/supremegamers/vendor_google_proprietary_widevine-prebuilt/archive/a1a19361d36311bee042da8cf4ced798d2c76d98.zip", "fed6898b5cfd2a908cb134df97802554"],
|
||||
"arm64-v8a": {
|
||||
"11": ["https://github.com/supremegamers/vendor_google_proprietary_widevine-prebuilt/archive/a1a19361d36311bee042da8cf4ced798d2c76d98.zip",
|
||||
"fed6898b5cfd2a908cb134df97802554"]
|
||||
}
|
||||
}
|
||||
dl_file_name = "widevine.zip"
|
||||
extract_to = "/tmp/widevineunpack"
|
||||
files = [
|
||||
"bin/hw/*widevine",
|
||||
"bin/move_widevine_data.sh",
|
||||
"etc/init/*widevine.rc",
|
||||
"etc/vintf/manifest/*widevine.xml",
|
||||
"lib/libwvhidl.so",
|
||||
"lib/libwvaidl.so",
|
||||
"lib/mediadrm",
|
||||
"lib64/libwvhidl.so",
|
||||
"lib64/libwvaidl.so",
|
||||
"lib64/mediadrm"
|
||||
]
|
||||
|
||||
def __init__(self, android_version) -> None:
|
||||
super().__init__()
|
||||
self.dl_link = self.dl_links[self.arch[0]][android_version][0]
|
||||
self.act_md5 = self.dl_links[self.arch[0]][android_version][1]
|
||||
self.android_version = android_version
|
||||
self.libdir = "lib64" if self.arch[1] == 64 else "lib"
|
||||
|
||||
def copy(self):
|
||||
name = re.findall("([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
|
||||
Logger.info("Copying widevine library files ...")
|
||||
shutil.copytree(os.path.join(self.extract_to, "vendor_google_proprietary_widevine-prebuilt-"+name,
|
||||
"prebuilts"), os.path.join(self.copy_dir, self.partition), dirs_exist_ok=True)
|
||||
if self.android_version == "13":
|
||||
try:
|
||||
os.symlink("libprotobuf-cpp-lite-3.9.1.so",
|
||||
os.path.join(self.copy_dir, self.partition, self.libdir, "libprotobuf-cpp-lite.so"))
|
||||
except FileExistsError:
|
||||
pass
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regression test for Magisk.setup() permissions on the /data/adb/magisk copy.
|
||||
|
||||
install() runs copy() -> extra1() -> set_perm(). set_perm() only walks self.files,
|
||||
which are paths under copy_dir (the overlay), so the copy that setup() makes into
|
||||
<data>/adb/magisk keeps the 0644 the binaries inherit from the apk extraction.
|
||||
|
||||
A non-executable /data/adb/magisk/magisk64 makes magisk_env() abort at post-fs-data.
|
||||
Root still works (it runs from the overlay copy), but Zygisk never initialises and
|
||||
the Magisk app reports "Zygisk: No" with no error explaining why.
|
||||
|
||||
Runs as an ordinary user, needs no Waydroid install and no extra dependencies:
|
||||
|
||||
python3 tests/test_magisk_setup_perms.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import stuff.magisk as magisk_module
|
||||
from stuff.magisk import Magisk
|
||||
|
||||
MAGISK_BINARIES = [
|
||||
"magisk64",
|
||||
"magiskinit",
|
||||
"magiskboot",
|
||||
"magiskpolicy",
|
||||
"busybox",
|
||||
"util_functions.sh",
|
||||
]
|
||||
|
||||
|
||||
def make_overlay(base):
|
||||
"""Mimic the tree copy() leaves behind: binaries written 0644 by shutil.copyfile."""
|
||||
magisk_dir = os.path.join(base, "system", "etc", "init", "magisk")
|
||||
os.makedirs(os.path.join(magisk_dir, "chromeos"), exist_ok=True)
|
||||
for name in MAGISK_BINARIES:
|
||||
path = os.path.join(magisk_dir, name)
|
||||
with open(path, "wb") as f:
|
||||
f.write(b"\x7fELF placeholder")
|
||||
os.chmod(path, 0o644)
|
||||
nested = os.path.join(magisk_dir, "chromeos", "futility")
|
||||
with open(nested, "wb") as f:
|
||||
f.write(b"placeholder")
|
||||
os.chmod(nested, 0o644)
|
||||
return magisk_dir
|
||||
|
||||
|
||||
def file_modes(root):
|
||||
modes = {}
|
||||
for dirpath, _dirnames, filenames in os.walk(root):
|
||||
for name in filenames:
|
||||
path = os.path.join(dirpath, name)
|
||||
modes[os.path.relpath(path, root)] = stat.S_IMODE(os.stat(path).st_mode)
|
||||
return modes
|
||||
|
||||
|
||||
def test_plain_copytree_loses_exec_bit():
|
||||
"""Document the original behaviour: a bare copytree leaves the binaries 0644."""
|
||||
base = tempfile.mkdtemp()
|
||||
try:
|
||||
src = make_overlay(base)
|
||||
dst = os.path.join(base, "data", "adb", "magisk")
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
shutil.copytree(src, dst, dirs_exist_ok=True)
|
||||
|
||||
modes = file_modes(dst)
|
||||
assert modes["magisk64"] == 0o644, (
|
||||
"expected the pre-fix behaviour to reproduce, got %o" % modes["magisk64"]
|
||||
)
|
||||
finally:
|
||||
shutil.rmtree(base, ignore_errors=True)
|
||||
|
||||
|
||||
def test_setup_makes_data_copy_executable():
|
||||
"""setup() must leave every file and dir in <data>/adb/magisk executable."""
|
||||
base = tempfile.mkdtemp()
|
||||
try:
|
||||
make_overlay(base)
|
||||
data_dir = os.path.join(base, "data")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
|
||||
original_get_data_dir = magisk_module.get_data_dir
|
||||
original_info = magisk_module.Logger.info
|
||||
magisk_module.get_data_dir = lambda: data_dir
|
||||
magisk_module.Logger.info = lambda *a, **k: None
|
||||
|
||||
class StubMagisk(Magisk):
|
||||
@property
|
||||
def copy_dir(self):
|
||||
return base
|
||||
|
||||
try:
|
||||
StubMagisk().setup()
|
||||
finally:
|
||||
magisk_module.get_data_dir = original_get_data_dir
|
||||
magisk_module.Logger.info = original_info
|
||||
|
||||
dst = os.path.join(data_dir, "adb", "magisk")
|
||||
modes = file_modes(dst)
|
||||
assert modes, "setup() copied nothing"
|
||||
|
||||
not_executable = {n: oct(m) for n, m in modes.items() if m != 0o755}
|
||||
assert not not_executable, "files left non-0755: %s" % not_executable
|
||||
|
||||
assert stat.S_IMODE(os.stat(dst).st_mode) == 0o755
|
||||
nested = os.path.join(dst, "chromeos")
|
||||
assert stat.S_IMODE(os.stat(nested).st_mode) == 0o755, "nested dir not fixed"
|
||||
finally:
|
||||
shutil.rmtree(base, ignore_errors=True)
|
||||
|
||||
|
||||
def test_setup_is_idempotent():
|
||||
base = tempfile.mkdtemp()
|
||||
try:
|
||||
make_overlay(base)
|
||||
data_dir = os.path.join(base, "data")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
|
||||
original_get_data_dir = magisk_module.get_data_dir
|
||||
original_info = magisk_module.Logger.info
|
||||
magisk_module.get_data_dir = lambda: data_dir
|
||||
magisk_module.Logger.info = lambda *a, **k: None
|
||||
|
||||
class StubMagisk(Magisk):
|
||||
@property
|
||||
def copy_dir(self):
|
||||
return base
|
||||
|
||||
try:
|
||||
StubMagisk().setup()
|
||||
StubMagisk().setup()
|
||||
finally:
|
||||
magisk_module.get_data_dir = original_get_data_dir
|
||||
magisk_module.Logger.info = original_info
|
||||
|
||||
dst = os.path.join(data_dir, "adb", "magisk")
|
||||
assert all(m == 0o755 for m in file_modes(dst).values())
|
||||
finally:
|
||||
shutil.rmtree(base, ignore_errors=True)
|
||||
|
||||
|
||||
def test_perm_helper_tolerates_missing_dir():
|
||||
Magisk().set_data_magisk_perm(os.path.join(tempfile.gettempdir(), "definitely-not-here"))
|
||||
|
||||
|
||||
def main():
|
||||
tests = [
|
||||
test_plain_copytree_loses_exec_bit,
|
||||
test_setup_makes_data_copy_executable,
|
||||
test_setup_is_idempotent,
|
||||
test_perm_helper_tolerates_missing_dir,
|
||||
]
|
||||
failed = 0
|
||||
for test in tests:
|
||||
try:
|
||||
test()
|
||||
except AssertionError as exc:
|
||||
failed += 1
|
||||
print("FAIL %s: %s" % (test.__name__, exc))
|
||||
except Exception as exc: # noqa: BLE001 - surface anything unexpected
|
||||
failed += 1
|
||||
print("ERROR %s: %r" % (test.__name__, exc))
|
||||
else:
|
||||
print("ok %s" % test.__name__)
|
||||
if failed:
|
||||
print("\n%d test(s) failed" % failed)
|
||||
return 1
|
||||
print("\nall %d tests passed" % len(tests))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,53 @@
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
# import dbus
|
||||
from tools.helper import run
|
||||
from tools.logger import Logger
|
||||
|
||||
# def DBusContainerService(object_path="/ContainerManager", intf="id.waydro.ContainerManager"):
|
||||
# return dbus.Interface(dbus.SystemBus().get_object("id.waydro.Container", object_path), intf)
|
||||
|
||||
# def DBusSessionService(object_path="/SessionManager", intf="id.waydro.SessionManager"):
|
||||
# return dbus.Interface(dbus.SessionBus().get_object("id.waydro.Session", object_path), intf)
|
||||
|
||||
# def use_dbus():
|
||||
# try:
|
||||
# DBusContainerService()
|
||||
# except:
|
||||
# return False
|
||||
# return True
|
||||
|
||||
def use_overlayfs():
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg")
|
||||
if not os.path.isfile(cfg_file):
|
||||
Logger.error("Cannot locate waydroid config file, reinit wayland and try again!")
|
||||
sys.exit(1)
|
||||
cfg.read(cfg_file)
|
||||
if "waydroid" not in cfg:
|
||||
Logger.error("Required entry in config was not found, Cannot continue!")
|
||||
if "mount_overlays" not in cfg["waydroid"]:
|
||||
return False
|
||||
if cfg["waydroid"]["mount_overlays"]=="True":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# def get_session():
|
||||
# return DBusContainerService().GetSession()
|
||||
|
||||
def stop():
|
||||
# if use_dbus():
|
||||
# session = DBusContainerService().GetSession()
|
||||
# if session:
|
||||
# DBusContainerService().Stop(False)
|
||||
# else:
|
||||
run(["waydroid", "container", "stop"])
|
||||
|
||||
|
||||
def is_running():
|
||||
return "Session:\tRUNNING" in run(["waydroid", "status"]).stdout.decode()
|
||||
|
||||
def upgrade():
|
||||
run(["waydroid", "upgrade", "-o"], ignore=r"\[.*\] Stopping container\n\[.*\] Starting container")
|
||||
@@ -0,0 +1,146 @@
|
||||
import gzip
|
||||
import os
|
||||
import re
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import requests
|
||||
from tools.logger import Logger
|
||||
from tqdm import tqdm
|
||||
import hashlib
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_download_dir():
|
||||
download_loc = ""
|
||||
if os.environ.get("XDG_CACHE_HOME", None) is None:
|
||||
download_loc = os.path.join('/', "home", os.environ.get(
|
||||
"SUDO_USER", os.environ["USER"]), ".cache", "waydroid-script", "downloads"
|
||||
)
|
||||
else:
|
||||
download_loc = os.path.join(
|
||||
os.environ["XDG_CACHE_HOME"], "waydroid-script", "downloads"
|
||||
)
|
||||
if not os.path.exists(download_loc):
|
||||
os.makedirs(download_loc)
|
||||
return download_loc
|
||||
|
||||
# not good
|
||||
def get_data_dir():
|
||||
return os.path.join('/', "home", os.environ.get("SUDO_USER", os.environ["USER"]), ".local", "share", "waydroid", "data")
|
||||
|
||||
# execute on host
|
||||
def run(args: list, env: Optional[str] = None, ignore: Optional[str] = None):
|
||||
result = subprocess.run(
|
||||
args=args,
|
||||
env=env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
# print(result.stdout.decode())
|
||||
if result.stderr:
|
||||
error = result.stderr.decode("utf-8")
|
||||
if ignore and re.match(ignore, error):
|
||||
return result
|
||||
Logger.error(error)
|
||||
raise subprocess.CalledProcessError(
|
||||
returncode=result.returncode,
|
||||
cmd=result.args,
|
||||
stderr=result.stderr
|
||||
)
|
||||
return result
|
||||
|
||||
# execute on waydroid shell
|
||||
def shell(arg: str, env: Optional[str] = None):
|
||||
a = subprocess.Popen(
|
||||
args=["sudo", "waydroid", "shell"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
subprocess.Popen(
|
||||
args=["echo", "export BOOTCLASSPATH=/apex/com.android.art/javalib/core-oj.jar:/apex/com.android.art/javalib/core-libart.jar:/apex/com.android.art/javalib/core-icu4j.jar:/apex/com.android.art/javalib/okhttp.jar:/apex/com.android.art/javalib/bouncycastle.jar:/apex/com.android.art/javalib/apache-xml.jar:/system/framework/framework.jar:/system/framework/ext.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/framework-atb-backward-compatibility.jar:/apex/com.android.conscrypt/javalib/conscrypt.jar:/apex/com.android.media/javalib/updatable-media.jar:/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar:/apex/com.android.os.statsd/javalib/framework-statsd.jar:/apex/com.android.permission/javalib/framework-permission.jar:/apex/com.android.sdkext/javalib/framework-sdkextensions.jar:/apex/com.android.wifi/javalib/framework-wifi.jar:/apex/com.android.tethering/javalib/framework-tethering.jar"],
|
||||
stdout=a.stdin,
|
||||
stdin=subprocess.PIPE
|
||||
).communicate()
|
||||
|
||||
if env:
|
||||
subprocess.Popen(
|
||||
args=["echo", env],
|
||||
stdout=a.stdin,
|
||||
stdin=subprocess.PIPE
|
||||
).communicate()
|
||||
|
||||
subprocess.Popen(
|
||||
args=["echo", arg],
|
||||
stdout=a.stdin,
|
||||
stdin=subprocess.PIPE
|
||||
).communicate()
|
||||
|
||||
a.stdin.close()
|
||||
if a.stderr.read():
|
||||
Logger.error(a.stderr.read().decode('utf-8'))
|
||||
raise subprocess.CalledProcessError(
|
||||
returncode=a.returncode,
|
||||
cmd=a.args,
|
||||
stderr=a.stderr
|
||||
)
|
||||
return a.stdout.read().decode("utf-8")
|
||||
|
||||
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()
|
||||
|
||||
mapping = {
|
||||
"i686": ("x86", 32),
|
||||
"x86_64": ("x86_64", 64),
|
||||
"aarch64": ("arm64-v8a", 64),
|
||||
"armv7l": ("armeabi-v7a", 32),
|
||||
"armv8l": ("armeabi-v7a", 32)
|
||||
}
|
||||
if machine in mapping:
|
||||
if mapping[machine] == "x86_64":
|
||||
with open("/proc/cpuinfo") as f:
|
||||
if "sse4_2" not in f.read():
|
||||
Logger.warning("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")
|
||||
|
||||
|
||||
def check_root():
|
||||
if os.geteuid() != 0:
|
||||
Logger.error("This script must be run as root. Aborting.")
|
||||
sys.exit(1)
|
||||
|
||||
def backup(path):
|
||||
gz_filename = path+".gz"
|
||||
with gzip.open(gz_filename, 'wb') as f_gz:
|
||||
with open(path, "rb") as f:
|
||||
f_gz.write(f.read())
|
||||
|
||||
def restore(path):
|
||||
gz_filename = path+".gz"
|
||||
with gzip.GzipFile(gz_filename) as f_gz:
|
||||
with open(path, "wb") as f:
|
||||
f.writelines(f_gz)
|
||||
@@ -0,0 +1,41 @@
|
||||
import configparser
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from tools.helper import run
|
||||
from tools.logger import Logger
|
||||
|
||||
def mount(image, mount_point):
|
||||
umount(mount_point, False)
|
||||
if not os.path.exists(mount_point):
|
||||
os.makedirs(mount_point)
|
||||
run(["mount", "-o", "rw", image, mount_point])
|
||||
|
||||
def umount(mount_point, exists=True):
|
||||
if not os.path.exists(mount_point):
|
||||
if not exists:
|
||||
return
|
||||
Logger.error("{} does not exist!".format(mount_point))
|
||||
raise FileNotFoundError()
|
||||
if not run(["mountpoint", mount_point]).returncode:
|
||||
run(["umount", mount_point])
|
||||
else:
|
||||
Logger.warning("{} is not a mount point".format(
|
||||
mount_point))
|
||||
|
||||
def resize(img_file, size):
|
||||
run(["sudo", "e2fsck", "-y", "-f", img_file], ignore="^e2fsck \d+\.\d+\.\d (.+)\n$")
|
||||
run(["sudo", "resize2fs", img_file, size], ignore="^resize2fs \d+\.\d+\.\d (.+)\n$")
|
||||
|
||||
def get_image_dir():
|
||||
# Read waydroid config to get image location
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg")
|
||||
if not os.path.isfile(cfg_file):
|
||||
Logger.error("Cannot locate waydroid config file, reinit wayland and try again!")
|
||||
sys.exit(1)
|
||||
cfg.read(cfg_file)
|
||||
if "waydroid" not in cfg:
|
||||
Logger.error("Required entry in config was not found, Cannot continue!") #magisk
|
||||
sys.exit(1)
|
||||
return cfg["waydroid"]["images_path"]
|
||||
@@ -0,0 +1,13 @@
|
||||
class Logger:
|
||||
|
||||
@staticmethod
|
||||
def error(str):
|
||||
print("\033[31m"+"ERROR: "+str+"\033[0m")
|
||||
|
||||
@staticmethod
|
||||
def info(str):
|
||||
print("\033[32m"+"INFO: "+"\033[0m"+str)
|
||||
|
||||
@staticmethod
|
||||
def warning(str):
|
||||
print("\033[33m"+"WARN: "+str+"\033[0m")
|
||||
@@ -1,594 +0,0 @@
|
||||
import configparser
|
||||
import sys
|
||||
import os
|
||||
import hashlib
|
||||
import shutil
|
||||
import zipfile
|
||||
import subprocess
|
||||
import argparse
|
||||
import platform
|
||||
from tqdm import tqdm
|
||||
import requests
|
||||
import re
|
||||
|
||||
download_loc = ""
|
||||
if os.environ.get("XDG_CACHE_HOME", None) is None:
|
||||
download_loc = os.path.join('/', "home", os.environ.get("SUDO_USER", os.environ["USER"]), ".cache", "waydroid_script", "downloads")
|
||||
else:
|
||||
os.path.join(os.environ["XDG_CACHE_HOME"], "waydroid_script", "downloads")
|
||||
|
||||
print(download_loc)
|
||||
|
||||
if not os.path.exists(download_loc):
|
||||
os.makedirs(download_loc)
|
||||
|
||||
def stop_waydroid():
|
||||
print("==> Stopping waydroid and unmounting already mounted images...")
|
||||
os.system("waydroid container stop &> /dev/null")
|
||||
os.system("umount /var/lib/waydroid/rootfs/vendor/waydroid.prop &> /dev/null")
|
||||
os.system("umount /var/lib/waydroid/rootfs/vendor &> /dev/null")
|
||||
os.system("umount /var/lib/waydroid/rootfs &> /dev/null")
|
||||
|
||||
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:
|
||||
print("==> Something went wrong while downloading")
|
||||
sys.exit(1)
|
||||
return md5
|
||||
|
||||
def get_image_dir():
|
||||
# Read waydroid config to get image location
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg")
|
||||
if not os.path.isfile(cfg_file):
|
||||
print("==> Cannot locate waydroid config file, reinit wayland and try again !")
|
||||
sys.exit(1)
|
||||
|
||||
cfg.read(cfg_file)
|
||||
if "waydroid" not in cfg:
|
||||
print("==> Required entry in config was not found, Cannot continue !s")
|
||||
sys.exit(1)
|
||||
return cfg["waydroid"]["images_path"]
|
||||
|
||||
def mount_image(image, mount_point):
|
||||
print("==> Unmounting .. ")
|
||||
try:
|
||||
subprocess.check_output(["losetup", "-D"], stderr=subprocess.STDOUT)
|
||||
subprocess.check_output(["umount", mount_point], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Warning: umount failed.. {} ".format(str(e.output.decode())))
|
||||
if not os.path.exists(mount_point):
|
||||
os.makedirs(mount_point)
|
||||
try:
|
||||
subprocess.check_output(["mount", "-o", "rw", image, mount_point], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Failed to mount system image... ! {}".format(str(e.output.decode())))
|
||||
sys.exit(1)
|
||||
|
||||
def resize_img(img_file, size):
|
||||
# Resize the system image
|
||||
print("==> Resizing system image....")
|
||||
try:
|
||||
subprocess.check_output(["e2fsck -y -f "+img_file], stderr=subprocess.STDOUT, shell=True)
|
||||
subprocess.check_output(["resize2fs '{}' {}".format(img_file, size)], stderr=subprocess.STDOUT, shell=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Failed to resize image '{}' .. ! {}".format(img_file, str(e.output.decode())))
|
||||
p = input("==> You can exit and retry with sudo or force continue (May fail installation !), Continue ? [y/N]: ")
|
||||
if not p.lower() == "y":
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def install_gapps():
|
||||
|
||||
dl_links = {
|
||||
"x86_64": ["https://master.dl.sourceforge.net/project/opengapps/x86_64/20220121/open_gapps-x86_64-10.0-pico-20220121.zip?viasf=1", "e8c9a7412f5712eea7948957a62a7d66"],
|
||||
"x86": ["https://udomain.dl.sourceforge.net/project/opengapps/x86/20220122/open_gapps-x86-10.0-pico-20220122.zip", "9e39e45584b7ade4529e6be654af7b81"],
|
||||
"aarch64": ["https://liquidtelecom.dl.sourceforge.net/project/opengapps/arm64/20220122/open_gapps-arm64-10.0-pico-20220122.zip", "8dfa6e76aeb2d1d5aed40b058e8a852c"],
|
||||
"arm": ["https://nav.dl.sourceforge.net/project/opengapps/arm/20220122/open_gapps-arm-10.0-pico-20220122.zip", "a48ccbd25eb0a3c5e30f5db5435f5536"]
|
||||
}
|
||||
if platform.machine() not in dl_links.keys():
|
||||
print("==> Unsupported architecture '{}' .. ".format(platform.machine()))
|
||||
sys.exit(1)
|
||||
google_apps_dl_link = dl_links[platform.machine()][0]
|
||||
dl_file_name = os.path.join(download_loc, "open_gapps.zip")
|
||||
act_md5 = dl_links[platform.machine()][1]
|
||||
loc_md5 = ""
|
||||
sys_image_mount = "/tmp/waydroidimage"
|
||||
extract_to = "/tmp/ogapps/extract"
|
||||
non_apks = [
|
||||
"defaultetc-common.tar.lz",
|
||||
"defaultframework-common.tar.lz",
|
||||
"googlepixelconfig-common.tar.lz"
|
||||
]
|
||||
skip = [
|
||||
"setupwizarddefault-x86_64.tar.lz",
|
||||
"setupwizardtablet-x86_64.tar.lz"
|
||||
]
|
||||
|
||||
if not os.path.exists(extract_to):
|
||||
os.makedirs(extract_to)
|
||||
if not os.path.exists(os.path.join(extract_to, "appunpack")):
|
||||
os.makedirs(os.path.join(extract_to, "appunpack"))
|
||||
|
||||
if os.path.isfile(dl_file_name):
|
||||
with open(dl_file_name,"rb") as f:
|
||||
bytes = f.read()
|
||||
loc_md5 = hashlib.md5(bytes).hexdigest()
|
||||
print("==> Excepted hash: {} | File hash: {}".format(act_md5, loc_md5))
|
||||
|
||||
|
||||
system_img = os.path.join(get_image_dir(), "system.img")
|
||||
if not os.path.isfile(system_img):
|
||||
print("The system image path '{}' from waydroid config is not valid !".format(system_img))
|
||||
sys.exit(1)
|
||||
print("==> Found system image: "+system_img)
|
||||
|
||||
img_size = int(os.path.getsize(system_img)/(1024*1024))
|
||||
|
||||
# Resize image to get some free space
|
||||
resize_img(system_img, "{}M".format(img_size+500))
|
||||
|
||||
# Mount the system image
|
||||
mount_image(system_img, sys_image_mount)
|
||||
|
||||
# Download the file if hash mismatches or if file does not exist
|
||||
while not os.path.isfile(dl_file_name) or loc_md5 != act_md5:
|
||||
if os.path.isfile(dl_file_name):
|
||||
os.remove(dl_file_name)
|
||||
print("==> OpenGapps zip not downloaded or hash mismatches, downloading now .....")
|
||||
loc_md5 = download_file(google_apps_dl_link, dl_file_name)
|
||||
|
||||
# Extract opengapps
|
||||
print("==> Extracting opengapps...")
|
||||
with zipfile.ZipFile(dl_file_name) as z:
|
||||
z.extractall(extract_to)
|
||||
|
||||
# Now copy the files
|
||||
for lz_file in os.listdir(os.path.join(extract_to, "Core")):
|
||||
for d in os.listdir(os.path.join(extract_to, "appunpack")):
|
||||
shutil.rmtree(os.path.join(extract_to, "appunpack", d))
|
||||
if lz_file not in skip:
|
||||
if lz_file not in non_apks:
|
||||
print("==> Processing app package : "+os.path.join(extract_to, "Core", lz_file))
|
||||
os.system("tar --lzip -xvf '{}' -C '{}'>/dev/null".format(os.path.join(extract_to, "Core", lz_file), os.path.join(extract_to, "appunpack")))
|
||||
app_name = os.listdir(os.path.join(extract_to, "appunpack"))[0]
|
||||
xx_dpi = os.listdir(os.path.join(extract_to, "appunpack", app_name))[0]
|
||||
app_priv = os.listdir(os.path.join(extract_to, "appunpack", app_name, "nodpi"))[0]
|
||||
app_src_dir = os.path.join(extract_to, "appunpack", app_name, xx_dpi, app_priv)
|
||||
for app in os.listdir(app_src_dir):
|
||||
shutil.copytree(os.path.join(app_src_dir, app), os.path.join(sys_image_mount, "system", "priv-app", app), dirs_exist_ok=True)
|
||||
else:
|
||||
print("==> Processing extra package : "+os.path.join(extract_to, "Core", lz_file))
|
||||
os.system("tar --lzip -xvf '{}' -C '{}'>/dev/null".format(os.path.join(extract_to, "Core", lz_file), os.path.join(extract_to, "appunpack")))
|
||||
app_name = os.listdir(os.path.join(extract_to, "appunpack"))[0]
|
||||
common_content_dirs = os.listdir(os.path.join(extract_to, "appunpack", app_name, "common"))
|
||||
for ccdir in common_content_dirs:
|
||||
shutil.copytree(os.path.join(extract_to, "appunpack", app_name, "common", ccdir), os.path.join(sys_image_mount, "system", ccdir), dirs_exist_ok=True)
|
||||
print("==> Unmounting .. ")
|
||||
try:
|
||||
subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Warning: umount failed.. {} ".format(str(e.output.decode())))
|
||||
print("==> OpenGapps installation complete try re init /restarting waydroid")
|
||||
print("==> Please note, google apps wont be usable without device registration !, Use --get-android-id for registration instructions")
|
||||
|
||||
|
||||
def get_android_id():
|
||||
try:
|
||||
if not os.path.isfile("/var/lib/waydroid/data/data/com.google.android.gsf/databases/gservices.db"):
|
||||
print("Cannot access gservices.db, make sure gapps is installed and waydroid was started at least once after installation and make sure waydroid is running !")
|
||||
sys.exit(1)
|
||||
sqs = """
|
||||
SELECT * FROM main WHERE name='android_id'
|
||||
"""
|
||||
queryout = subprocess.check_output(["sqlite3", "/var/lib/waydroid/data/data/com.google.android.gsf/databases/gservices.db", sqs.strip()], stderr=subprocess.STDOUT)
|
||||
print(queryout.decode().replace("android_id|", "").strip())
|
||||
print(" ^----- Open https://google.com/android/uncertified/?pli=1")
|
||||
print(" Login with your google id then submit the form with id shown above")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Error getting id... '{}' ".format(str(e.output.decode())))
|
||||
|
||||
def install_ndk():
|
||||
sys_image_mount = "/tmp/waydroidimage"
|
||||
ndk_zip_url = "https://github.com/newbit1/libndk_translation_Module/archive/c6077f3398172c64f55aad7aab0e55fad9110cf3.zip"
|
||||
dl_file_name = os.path.join(download_loc, "libndktranslation.zip")
|
||||
extract_to = "/tmp/libndkunpack" #All catalog files will be marked as executable!
|
||||
act_md5 = "5e8e0cbde0e672fdc2b47f20a87472fd"
|
||||
loc_md5 = ""
|
||||
apply_props = {
|
||||
"ro.product.cpu.abilist": "x86_64,x86,armeabi-v7a,armeabi", #arm64-v8a,
|
||||
"ro.product.cpu.abilist32": "x86,armeabi-v7a,armeabi",
|
||||
"ro.product.cpu.abilist64": "x86_64", #,arm64-v8a",
|
||||
"ro.dalvik.vm.native.bridge": "libndk_translation.so",
|
||||
"ro.enable.native.bridge.exec": "1",
|
||||
"ro.ndk_translation.version": "0.2.2",
|
||||
"ro.dalvik.vm.isa.arm": "x86",
|
||||
"ro.dalvik.vm.isa.arm64": "x86_64"
|
||||
}
|
||||
init_rc_component = """
|
||||
on early-init
|
||||
mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
|
||||
|
||||
on property:ro.enable.native.bridge.exec=1
|
||||
exec -- /system/bin/sh -c "cat /system/etc/binfmt_misc/arm_exe > /proc/sys/fs/binfmt_misc/register"
|
||||
exec -- /system/bin/sh -c "cat /system/etc/binfmt_misc/arm_dyn >> /proc/sys/fs/binfmt_misc/register"
|
||||
exec -- /system/bin/sh -c "cat /system/etc/binfmt_misc/arm64_exe >> /proc/sys/fs/binfmt_misc/register"
|
||||
exec -- /system/bin/sh -c "cat /system/etc/binfmt_misc/arm64_dyn >> /proc/sys/fs/binfmt_misc/register"
|
||||
"""
|
||||
if os.path.isfile(dl_file_name):
|
||||
with open(dl_file_name,"rb") as f:
|
||||
bytes = f.read()
|
||||
loc_md5 = hashlib.md5(bytes).hexdigest()
|
||||
|
||||
system_img = os.path.join(get_image_dir(), "system.img")
|
||||
if not os.path.isfile(system_img):
|
||||
print("The system image path '{}' from waydroid config is not valid !".format(system_img))
|
||||
sys.exit(1)
|
||||
print("==> Found system image: "+system_img)
|
||||
|
||||
# Resize rootfs
|
||||
img_size = int(os.path.getsize(system_img)/(1024*1024))
|
||||
|
||||
# Resize image to get some free space
|
||||
resize_img(system_img, "{}M".format(img_size+50))
|
||||
|
||||
# Mount the system image
|
||||
mount_image(system_img, sys_image_mount)
|
||||
|
||||
# Download the file if hash mismatches or if file does not exist
|
||||
while not os.path.isfile(dl_file_name) or loc_md5 != act_md5:
|
||||
if os.path.isfile(dl_file_name):
|
||||
os.remove(dl_file_name)
|
||||
print("==> NDK Translation zip not downloaded or hash mismatches, downloading now .....")
|
||||
loc_md5 = download_file(ndk_zip_url, dl_file_name)
|
||||
|
||||
# Extract ndk files
|
||||
print("==> Extracting archive...")
|
||||
with zipfile.ZipFile(dl_file_name) as z:
|
||||
z.extractall(extract_to)
|
||||
|
||||
#Mark ndk files as executable
|
||||
print("==> Chmodding...")
|
||||
try: os.system("chmod +x "+extract_to+" -R")
|
||||
except: print("==> Couldn't mark files as executable!")
|
||||
|
||||
# Copy library file
|
||||
print("==> Copying library files ...")
|
||||
shutil.copytree(os.path.join(extract_to, "libndk_translation_Module-c6077f3398172c64f55aad7aab0e55fad9110cf3", "system"), os.path.join(sys_image_mount, "system"), dirs_exist_ok=True)
|
||||
|
||||
# Add entries to build.prop
|
||||
print("==> Adding arch in build.prop")
|
||||
with open(os.path.join(sys_image_mount, "system", "build.prop"), "r") as propfile:
|
||||
prop_content = propfile.read()
|
||||
for key in apply_props:
|
||||
if key not in prop_content:
|
||||
prop_content = prop_content+"\n{key}={value}".format(key=key, value=apply_props[key])
|
||||
else:
|
||||
p = re.compile(r"^{key}=.*$".format(key=key), re.M)
|
||||
prop_content = re.sub(p, "{key}={value}".format(key=key, value=apply_props[key]), prop_content)
|
||||
with open(os.path.join(sys_image_mount, "system", "build.prop"), "w") as propfile:
|
||||
propfile.write(prop_content)
|
||||
|
||||
|
||||
# Add entry to init.rc
|
||||
print("==> Adding entry to init.rc")
|
||||
# Check if init.rc is located in Android 11's path
|
||||
init_path = os.path.join(sys_image_mount, "system", "etc", "init", "hw", "init.rc")
|
||||
if not os.path.isfile(init_path):
|
||||
# init.rc not found, assuming it's located in the root folder (Android 10 and older)
|
||||
init_path = os.path.join(sys_image_mount, "init.rc")
|
||||
with open(init_path, "r") as initfile:
|
||||
initcontent = initfile.read()
|
||||
if init_rc_component not in initcontent:
|
||||
initcontent=initcontent+init_rc_component
|
||||
with open(init_path, "w") as initfile:
|
||||
initfile.write(initcontent)
|
||||
|
||||
# Unmount and exit
|
||||
print("==> Unmounting .. ")
|
||||
try:
|
||||
subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Warning: umount failed.. {} ".format(str(e.output.decode())))
|
||||
|
||||
print("==> libndk translation installed ! Restart waydroid service to apply changes !")
|
||||
|
||||
|
||||
def install_houdini():
|
||||
sys_image_mount = "/tmp/waydroidimage"
|
||||
houdini_zip_url = "https://raw.githubusercontent.com/casualsnek/miscpackages/main/libhoudini_a11.zip"
|
||||
dl_file_name = os.path.join(download_loc, "libhoudini.zip")
|
||||
extract_to = "/tmp/houdiniunpack" #All catalog files will be marked as executable!
|
||||
act_md5 = "c9a80831641de8fd44ccf93a0ad8b585"
|
||||
loc_md5 = ""
|
||||
|
||||
apply_props = {
|
||||
"ro.product.cpu.abilist": "x86_64,x86,arm64-v8a,armeabi-v7a,armeabi",
|
||||
"ro.product.cpu.abilist32": "x86,armeabi-v7a,armeabi",
|
||||
"ro.product.cpu.abilist64": "x86_64,arm64-v8a",
|
||||
"ro.dalvik.vm.native.bridge": "libhoudini.so",
|
||||
"ro.enable.native.bridge.exec": "1",
|
||||
"ro.dalvik.vm.isa.arm": "x86",
|
||||
"ro.dalvik.vm.isa.arm64": "x86_64"
|
||||
}
|
||||
init_rc_component = """
|
||||
on early-init
|
||||
mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
|
||||
|
||||
on property:ro.enable.native.bridge.exec=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"
|
||||
"""
|
||||
|
||||
if os.path.isfile(dl_file_name):
|
||||
with open(dl_file_name,"rb") as f:
|
||||
bytes = f.read()
|
||||
loc_md5 = hashlib.md5(bytes).hexdigest()
|
||||
|
||||
system_img = os.path.join(get_image_dir(), "system.img")
|
||||
if not os.path.isfile(system_img):
|
||||
print("The system image path '{}' from waydroid config is not valid !".format(system_img))
|
||||
sys.exit(1)
|
||||
print("==> Found system image: "+system_img)
|
||||
|
||||
img_size = int(os.path.getsize(system_img)/(1024*1024))
|
||||
|
||||
# Resize image to get some free space
|
||||
resize_img(system_img, "{}M".format(img_size+300))
|
||||
|
||||
|
||||
# Mount the system image
|
||||
mount_image(system_img, sys_image_mount)
|
||||
|
||||
# Download the file if hash mismatches or if file does not exist
|
||||
while not os.path.isfile(dl_file_name) or loc_md5 != act_md5:
|
||||
if os.path.isfile(dl_file_name):
|
||||
os.remove(dl_file_name)
|
||||
print("==> libhoudini zip not downloaded or hash mismatches, downloading now .....")
|
||||
loc_md5 = download_file(houdini_zip_url, dl_file_name)
|
||||
|
||||
# Extract ndk files
|
||||
print("==> Extracting archive...")
|
||||
with zipfile.ZipFile(dl_file_name) as z:
|
||||
z.extractall(extract_to)
|
||||
|
||||
# Mark libhoudini files as executable
|
||||
print("==> Chmodding...")
|
||||
try: os.system("chmod +x "+extract_to+" -R")
|
||||
except: print("==> Couldn't mark files as executable!")
|
||||
|
||||
# Copy library file
|
||||
print("==> Copying library files ...")
|
||||
shutil.copytree(os.path.join(extract_to, "system"), os.path.join(sys_image_mount, "system"), dirs_exist_ok=True)
|
||||
|
||||
# Add entries to build.prop
|
||||
print("==> Adding arch in build.prop")
|
||||
with open(os.path.join(sys_image_mount, "system", "build.prop"), "r") as propfile:
|
||||
prop_content = propfile.read()
|
||||
for key in apply_props:
|
||||
if key not in prop_content:
|
||||
prop_content = prop_content+"\n{key}={value}".format(key=key, value=apply_props[key])
|
||||
else:
|
||||
p = re.compile(r"^{key}=.*$".format(key=key), re.M)
|
||||
prop_content = re.sub(p, "{key}={value}".format(key=key, value=apply_props[key]), prop_content)
|
||||
with open(os.path.join(sys_image_mount, "system", "build.prop"), "w") as propfile:
|
||||
propfile.write(prop_content)
|
||||
|
||||
# Add entry to init.rc
|
||||
print("==> Adding entry to init.rc")
|
||||
# Check if init.rc is located in Android 11's path
|
||||
init_path = os.path.join(sys_image_mount, "system", "etc", "init", "hw", "init.rc")
|
||||
if not os.path.isfile(init_path):
|
||||
# init.rc not found, assuming it's located in the root folder (Android 10 and older)
|
||||
init_path = os.path.join(sys_image_mount, "init.rc")
|
||||
with open(init_path, "r") as initfile:
|
||||
initcontent = initfile.read()
|
||||
if init_rc_component not in initcontent:
|
||||
initcontent=initcontent+init_rc_component
|
||||
with open(init_path, "w") as initfile:
|
||||
initfile.write(initcontent)
|
||||
|
||||
# Unmount and exit
|
||||
print("==> Unmounting .. ")
|
||||
try:
|
||||
subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Warning: umount failed.. {} ".format(str(e.output.decode())))
|
||||
|
||||
print("==> libhoudini translation installed ! Restart waydroid service to apply changes !")
|
||||
|
||||
|
||||
def install_magisk():
|
||||
dl_link = "https://github.com/topjohnwu/Magisk/releases/download/v20.4/Magisk-v20.4.zip"
|
||||
busybox_dl_link = "https://github.com/Gnurou/busybox-android/raw/master/busybox-android"
|
||||
busybox_dl_file_name = os.path.join(download_loc, "busybox-android")
|
||||
dl_file_name = os.path.join(download_loc, "magisk.zip")
|
||||
extract_to = "/tmp/magisk_unpack"
|
||||
act_md5 = "9503fc692e03d60cb8897ff2753c193f"
|
||||
busybox_act_md5 = "2e43cc2e8f44b83f9029a6561ce5d8b9"
|
||||
sys_image_mount = "/tmp/waydroidimage"
|
||||
loc_md5 = ""
|
||||
busybox_loc_md5 = ""
|
||||
magisk_init = """#!/system/bin/sh
|
||||
mount -o remount,rw /
|
||||
rm /sbin/magisk /sbin/magiskpolicy /sbin/magiskinit
|
||||
cp /magiskinit /sbin/magiskinit
|
||||
ln -s /sbin/magiskinit /sbin/magisk
|
||||
ln -s /sbin/magiskinit /sbin/magiskpolicy
|
||||
mkdir -p /data/adb/magisk
|
||||
cp /busybox /data/adb/magisk/busybox
|
||||
cp /util_functions.sh /data/adb/magisk/util_functions.sh
|
||||
cp /boot_patch.sh /data/adb/magisk/boot_patch.sh
|
||||
cp /addon.d.sh /data/adb/magisk/addon.d.sh
|
||||
magisk -c >&2
|
||||
ln -sf /data /sbin/.magisk/mirror/data
|
||||
ln -sf /vendor /sbin/.magisk/mirror/vendor
|
||||
magisk --post-fs-data
|
||||
sleep 1
|
||||
magisk --service
|
||||
magisk --boot-complete
|
||||
mount -o remount,ro /
|
||||
"""
|
||||
init_rc_component = """on property:dev.bootcomplete=1
|
||||
start magisk
|
||||
|
||||
service magisk /system/bin/init-magisk.sh
|
||||
class main
|
||||
user root
|
||||
group root
|
||||
oneshot
|
||||
"""
|
||||
if os.path.isfile(dl_file_name):
|
||||
with open(dl_file_name,"rb") as f:
|
||||
bytes = f.read()
|
||||
loc_md5 = hashlib.md5(bytes).hexdigest()
|
||||
|
||||
if os.path.isfile(busybox_dl_file_name):
|
||||
with open(busybox_dl_file_name,"rb") as f:
|
||||
bytes = f.read()
|
||||
busybox_loc_md5 = hashlib.md5(bytes).hexdigest()
|
||||
|
||||
|
||||
system_img = os.path.join(get_image_dir(), "system.img")
|
||||
if not os.path.isfile(system_img):
|
||||
print("The system image path '{}' from waydroid config is not valid !".format(system_img))
|
||||
sys.exit(1)
|
||||
print("==> Found system image: " + system_img)
|
||||
|
||||
img_size = int(os.path.getsize(system_img)/(1024*1024))
|
||||
|
||||
# Resize image to get some free space
|
||||
resize_img(system_img, "{}M".format(img_size+50))
|
||||
|
||||
# Mount the system image
|
||||
mount_image(system_img, sys_image_mount)
|
||||
|
||||
# Download magisk
|
||||
while not os.path.isfile(dl_file_name) or loc_md5 != act_md5:
|
||||
if os.path.isfile(dl_file_name):
|
||||
os.remove(dl_file_name)
|
||||
print("==> Magisk zip not downloaded or hash mismatches, downloading now .....")
|
||||
loc_md5 = download_file(dl_link, dl_file_name)
|
||||
|
||||
# Download busybox android binary
|
||||
while not os.path.isfile(busybox_dl_file_name) or busybox_loc_md5 != busybox_act_md5:
|
||||
if os.path.isfile(busybox_dl_file_name):
|
||||
os.remove(busybox_dl_file_name)
|
||||
print("==> BusyBox binary not downloaded or hash mismatches, downloading now .....")
|
||||
busybox_loc_md5 = download_file(busybox_dl_link, busybox_dl_file_name)
|
||||
|
||||
# Extract magisk files
|
||||
print("==> Extracting archive...")
|
||||
with zipfile.ZipFile(dl_file_name) as z:
|
||||
z.extractall(extract_to)
|
||||
|
||||
# Now setup and install magisk binary and app
|
||||
print("==> Installing magisk now ...")
|
||||
with open(os.path.join(sys_image_mount, "system", "bin", "init-magisk.sh"), "w") as imf:
|
||||
imf.write(magisk_init)
|
||||
os.system("chmod 755 {}".format(os.path.join(sys_image_mount, "system", "bin", "init-magisk.sh")))
|
||||
arch_dir = "x86" if "x86" in platform.machine() else "arm"
|
||||
arch = "64" if "64" in platform.machine() else ""
|
||||
shutil.copyfile(os.path.join(extract_to, arch_dir, "magiskinit{arch}".format(arch=arch)),
|
||||
os.path.join(sys_image_mount, "sbin", "magiskinit"))
|
||||
shutil.copyfile(os.path.join(extract_to, arch_dir, "magiskinit{arch}".format(arch=arch)),
|
||||
os.path.join(sys_image_mount, "magiskinit"))
|
||||
os.system("chmod 755 {} & chmod 755 {}".format(os.path.join(sys_image_mount, "sbin", "magiskinit"), os.path.join(sys_image_mount, "magiskinit")))
|
||||
|
||||
# Copy busybox
|
||||
print("==> Installing BusyBox")
|
||||
shutil.copyfile(busybox_dl_file_name, os.path.join(sys_image_mount, "busybox"))
|
||||
os.system("chmod 755 {}".format(os.path.join(sys_image_mount, "busybox")))
|
||||
|
||||
# Copy files from common directory
|
||||
for file in ["util_functions.sh", "boot_patch.sh", "addon.d.sh"]:
|
||||
shutil.copyfile(os.path.join(extract_to, "common", file),
|
||||
os.path.join(sys_image_mount, file))
|
||||
os.system("chmod 755 {}".format(os.path.join(sys_image_mount, file)))
|
||||
|
||||
# Create symlinks
|
||||
print("==> Creating symlinks")
|
||||
os.system("cd {root}/sbin && ln -sf magiskinit magiskinit >> /dev/null 2>&1".format(root=sys_image_mount))
|
||||
os.system("cd {root}/sbin && ln -sf magiskinit magisk >> /dev/null 2>&1".format(root=sys_image_mount))
|
||||
print("==> magiskinit -> magisk")
|
||||
os.system("cd {root}/sbin && ln -sf /magiskinit magiskpolicy >> /dev/null 2>&1".format(root=sys_image_mount))
|
||||
print("==> magiskinit -> magiskpolicy")
|
||||
|
||||
# Add entry to init.rc
|
||||
print("==> Adding entry to init.rc")
|
||||
with open(os.path.join(sys_image_mount, "init.rc"), "r") as initfile:
|
||||
initcontent = initfile.read()
|
||||
if init_rc_component not in initcontent:
|
||||
initcontent=initcontent+init_rc_component
|
||||
with open(os.path.join(sys_image_mount, "init.rc"), "w") as initfile:
|
||||
initfile.write(initcontent)
|
||||
|
||||
# Install Magisk apk
|
||||
if not os.path.exists(os.path.join(sys_image_mount, "system", "priv-app", "Magisk")):
|
||||
os.makedirs(os.path.join(sys_image_mount, "system", "priv-app", "Magisk"))
|
||||
shutil.copyfile(os.path.join(extract_to, "common", "magisk.apk"),
|
||||
os.path.join(sys_image_mount, "system", "priv-app", "Magisk", "magisk.apk"))
|
||||
|
||||
# Unmount and exit
|
||||
print("==> Unmounting .. ")
|
||||
try:
|
||||
subprocess.check_output(["umount", sys_image_mount], stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("==> Warning: umount failed.. {} ".format(str(e.output.decode())))
|
||||
|
||||
print("==> Magisk was installed ! Restart waydroid service to apply changes !")
|
||||
def main():
|
||||
about = """
|
||||
WayDroid Helper script v0.3
|
||||
Does stuff like installing Gapps, Installing NDK Translation and getting Android ID for device registration.
|
||||
Use -h flag for help !
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description=about, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument('-g', '--install-gapps',
|
||||
dest='install',
|
||||
help='Install OpenGapps to waydroid',
|
||||
action='store_true')
|
||||
parser.add_argument('-n', '--install-ndk-translation',
|
||||
dest='installndk',
|
||||
help='Install experimental libndk translation files',
|
||||
action='store_true')
|
||||
parser.add_argument('-i', '--get-android-id', dest='getid',
|
||||
help='Displays your android id for manual registration',
|
||||
action='store_true')
|
||||
parser.add_argument('-m', '--install-magisk', dest='magisk',
|
||||
help='Attempts to install Magisk ( Bootless )',
|
||||
action='store_true')
|
||||
parser.add_argument('-l', '--install-libhoudini', dest='houdini',
|
||||
help='Install libhoudini for arm translation',
|
||||
action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.install:
|
||||
stop_waydroid()
|
||||
install_gapps()
|
||||
elif args.installndk:
|
||||
stop_waydroid()
|
||||
install_ndk()
|
||||
elif args.getid:
|
||||
get_android_id()
|
||||
elif args.magisk:
|
||||
install_magisk()
|
||||
elif args.houdini:
|
||||
install_houdini()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||