Compare commits

..
12 Commits
Author SHA1 Message Date
wbruno b20483dd9a Merge pull request 'Update SmartDock to new version' (#218) from rduttshukla/feat/smartdock-update-and-permission-fix into main
Reviewed-on: #218
2026-10-12 14:01:14 -04:00
wbruno 8020dbd8fc Merge pull request 'Fix forgotten commas in microg.py' (#270) from MarSoft/patch-1 into main
Reviewed-on: #270
2026-10-12 14:00:52 -04:00
wbruno b7e1c37e14 Merge pull request 'Fix /data/adb/magisk permissions so Zygisk can initialise' (#284) from MisterWizzard/fix/magisk-data-adb-permissions into main
Reviewed-on: #284
2026-10-12 14:00:19 -04:00
wbruno 2f6547f873 Merge pull request 'feat: add device spoof to fix Play Store device compatibility' (#275) from EsymDnB/main into main
Reviewed-on: #275
2026-10-12 13:59:31 -04:00
Suggon 48dbfaf34a Update houdini library with extended expiry date (July 1, 2028)
Intel's libhoudini library includes a built-in "expiry date" that
prevents execution after a certain date. The previous versions expired
on January 1, 2026 (#256) and September 1, 2026 respectively.

This update refreshes the library to a newer build per
https://github.com/supremegamers/vendor_intel_proprietary_houdini/issues/6
and has an extended expiry date of July 1, 2028.
2026-09-11 14:56:36 +02:00
MisterWizzardandClaude Opus 5 b09e018894 Fix /data/adb/magisk permissions so Zygisk can initialise
General.install() runs copy() -> extra1() -> set_perm(). set_perm() only walks
self.files, which are paths under copy_dir (the overlay), so the copy that
Magisk.setup() makes into <data_dir>/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, because that runs from the overlay copy, but
Zygisk never initialises: the Magisk app reports "Zygisk: No" no matter what the
zygisk setting says, and no error is surfaced to explain why. This breaks every
Zygisk module (LSPosed, Shamiko, PlayIntegrityFix, ...).

Fix setup() to apply the same 0755 to its own destination that
Magisk.set_path_perm() already applies to magisk paths.

Adds a regression test that passes with the fix and fails without it. It needs no
Waydroid install, no root and no extra dependencies:

    python3 tests/test_magisk_setup_perms.py

Fixes #283

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 09:49:05 +10:00
mcheesemanandClaude Sonnet 4.6 3bc2b310a3 feat: add device spoof to fix Play Store device compatibility
Adds a device_spoof module that writes Android device identity
properties into waydroid.cfg, enabling games like Destiny Rising
that reject Waydroid's generic device profile. Includes Pixel 6
and Samsung Galaxy S21 profiles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 20:03:41 +08:00
Семён Марьясин 963e3511fe Fix forgotten commas in microg.py
Choosing Minimal variants resulted in this without my fix:

  File "/nix/store/2a3cl1fvl0418qmkrvndx4si29w9yvv5-waydroid-script-0-unstable-2026-01-05/lib/python3.13/site-packages/stuff/microg.py", line 109, in __init__
    self.act_md5 = self.dl_links[variant][1]
                   ~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range
2026-04-14 19:04:01 +03:00
ayasa520 d5289cfd89 Update libhoudini (#256)
* Update download link for Houdini version 13
2026-01-05 09:56:03 +01:00
NullBite ddaa6b190f Add Nix flake and improve derivation
This adds a Nix flake to the repo as well as updates the derivation so
that it includes `lzip` as a dependency
2025-11-17 10:08:53 +01:00
Gerry Bagaskoro Putro 15020ec20a fix(nodataperm): prevent crash when uninstalling on Android 13 2025-11-11 19:37:38 +01:00
Rishi 2d3bd9432f Update SmartDock to new version and add WRITE_SECURE_SETTINGS to fix bootloop 2025-03-22 17:09:09 +05:30
25 changed files with 562 additions and 260 deletions
+41 -57
View File
@@ -20,7 +20,9 @@ sudo venv/bin/python3 main.py
![image-20230430013148814](assets/img/README/image-20230430013148814.png)
## Command line
## Command Line
```bash
git clone https://github.com/casualsnek/waydroid_script
@@ -40,72 +42,60 @@ 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:
### Arch, Manjaro and EndeavourOS based distributions:
sudo pacman -S lzip
### Debian and Ubuntu-based distributions:
### Debian and Ubuntu based distributions:
sudo apt install lzip
### RHEL, Fedora, and Rocky-based distributions:
### RHEL, Fedora and Rocky based distributions:
sudo dnf install lzip
### openSUSE-based distributions:
### openSUSE based distributions:
sudo zypper install lzip
## Install Open GApps
## Install OpenGapps
![](assets/1.png)
Open the terminal and switch to the directory where "main.py" is located, then run:
Open terminal and switch to the directory where "main.py" is located then run:
sudo venv/bin/python3 main.py install gapps
Then launch Waydroid with:
Then launch waydroid with:
waydroid show-full-ui
After Waydroid has finished booting, open the terminal and switch to the directory where "main.py" is located, then run:
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!
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 the device to get registered. Then clear Google Play Service's cache and try logging
in!
## Install Magisk
![](assets/2.png)
Open the terminal and switch to the directory where "main.py" is located, then run:
Open terminal and switch to directory where "main.py" is located then run:
sudo venv/bin/python3 main.py install magisk
Magisk will be installed on next boot!
Magisk will be installed on next boot!
Zygisk and modules like LSPosed should work now.
If you want to update Magisk, Please use `Direct Install into system partition` or run this script again.
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
This script only focuses on Magisk installation, if you need more management, please check https://github.com/nitanmarcel/waydroid-magisk
## Install libndk ARM translation
## Install libndk arm translation
libndk_translation from guybrush firmware.
libndk_translation from guybrush firmware.
libndk seems to have better performance than libhoudini on AMD.
Open the terminal and switch to the directory where "main.py" is located, then run:
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
## Install libhoudini arm translation
Intel's libhoudini for intel/AMD x86 CPU, pulled from Microsoft's WSA 11 image
@@ -113,7 +103,7 @@ houdini version: 11.0.1b_y.38765.m
houdini64 version: 11.0.1b_z.38765.m
Open the terminal and switch to the directory where "main.py" is located, then run:
Open terminal and switch to directory where "main.py" is located then run:
sudo venv/bin/python3 main.py install libhoudini
@@ -121,7 +111,7 @@ Open the terminal and switch to the directory where "main.py" is located, then r
![](assets/3.png)
Open the terminal and switch to the directory where "main.py" is located, then run:
Open terminal and switch to directory where "main.py" is located then run:
sudo venv/bin/python3 main.py install widevine
@@ -130,38 +120,34 @@ Open the terminal and switch to the directory where "main.py" is located, then r
![](assets/4.png)
![](assets/5.png)
Open the terminal and switch to the directory where "main.py" is located, then run:
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 the terminal and switch to the directory where "main.py" is located, then run:
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.
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.
![](assets/6.png)
Open the terminal and switch to the directory where "main.py" is located, then run:
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.
**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 is much less risky.
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
@@ -172,10 +158,10 @@ 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/device_generic_common/commit/2d47891376c96011b2ee3c1ccef61cb48e15aed6
- https://github.com/supremegamers/android_frameworks_base/commit/24a08bf800b2e461356a9d67d04572bb10b0e819
## Install microG, Aurora Store, and Aurora Droid
## Install microG, Aurora Store and Aurora Droid
![](assets/7.png)
@@ -183,8 +169,7 @@ chmod 777 -R /mnt/*/*/*/*/Android/obb
sudo venv/bin/python3 main.py install microg
```
## Hide status bar
## Hide Status Bar
Before
![Before](assets/8.png)
@@ -195,27 +180,26 @@ After
sudo venv/bin/python3 main.py hack hidestatusbar
```
## Get Android ID for device registration
You need to register your device with its ID 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 the terminal and switch to the directory where "main.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 venv/bin/python3 main.py certified
Star this repository if you find this useful, if you encounter a problem, create an issue on GitHub!
Star this repository if you find this useful, if you encounter problem create an issue on GitHub!
## Error handling
## 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)
- [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)
- [wd-scripts](https://github.com/electrikjesus/wd-scripts/)
+4 -22
View File
@@ -1,22 +1,4 @@
with (import <nixpkgs> {});
stdenv.mkDerivation {
name = "waydroid_script";
buildInputs = [
(python3.withPackages(ps: with ps; [ tqdm requests inquirerpy ]))
];
src = ./.;
postPatch = ''
patchShebangs main.py
'';
installPhase = ''
mkdir -p $out/libexec
cp -r . $out/libexec/waydroid_script
mkdir -p $out/bin
ln -s $out/libexec/waydroid_script/main.py $out/bin/waydroid_script
'';
}
let
pkgs = import <nixpkgs> {};
in
pkgs.callPackage ./package.nix { }
Generated
+41
View File
@@ -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
}
+21
View File
@@ -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;
});
};
}
+40 -20
View File
@@ -1,14 +1,11 @@
#!/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 InquirerPy import inquirer
from InquirerPy.base.control import Choice
import tools.helper as helper
from stuff.android_id import AndroidId
from stuff.fdroidpriv import FDroidPriv
from stuff.gapps import Gapps
from stuff.general import General
from stuff.hidestatusbar import HideStatusBar
@@ -20,8 +17,14 @@ 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
@@ -30,7 +33,7 @@ def get_certified(args):
def mount(partition, copy_dir):
img = os.path.join(images.get_image_dir(), partition + ".img")
img = os.path.join(images.get_image_dir(), partition+".img")
mount_point = ""
if partition == "system":
mount_point = os.path.join(copy_dir)
@@ -41,9 +44,9 @@ def mount(partition, copy_dir):
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)
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)
@@ -87,6 +90,8 @@ def install_app(args):
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"
@@ -138,6 +143,8 @@ def remove_app(args):
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:
@@ -227,7 +234,7 @@ def interact():
if not action:
exit()
install_choices = ["gapps", "microg", "libndk", "libhoudini", "magisk", "smartdock", "fdroidpriv", "widevine",]
install_choices = ["gapps", "microg", "libndk", "libhoudini", "magisk", "smartdock", "fdroidpriv", "widevine", "device_spoof"]
hack_choices = []
if android_version=="11":
hack_choices.extend(["nodataperm", "hidestatusbar"])
@@ -249,6 +256,13 @@ def interact():
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":
@@ -260,7 +274,7 @@ def interact():
choices=[*install_choices, *hack_choices]
).execute()
args.app = apps
args.microg_variant = "Standard"
args.microg_variant="Standard"
remove_app(args)
elif action == "Hack":
apps = inquirer.checkbox(
@@ -277,8 +291,9 @@ def interact():
def main():
parser = argparse.ArgumentParser(description='''Does stuff like installing Aapps, installing Magisk, installing
NDK Translation, and getting Android ID for device registration. Use -h flag for help!''')
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',
@@ -293,7 +308,7 @@ def main():
certified.set_defaults(func=get_certified)
install_choices = ["gapps", "microg", "libndk", "libhoudini",
"magisk", "mitm", "smartdock", "widevine"]
"magisk", "mitm", "smartdock", "widevine", "device_spoof"]
hack_choices = ["nodataperm", "hidestatusbar"]
micrg_variants = ["Standard", "NoGoolag", "UNLP", "Minimal", "MinimalIAP"]
remove_choices = install_choices
@@ -307,10 +322,10 @@ def main():
install_help = """
gapps: Install Open GApps (Android 11) or MindTheGapps (Android 13)
microg: Add microG, Aurora Store, and Aurora Droid to WayDroid
libndk: Add libndk ARM translation, better for AMD CPUs
libhoudini: Add libhoudini ARM translation, better for Intel CPUs
magisk: Install Magisk Delta to Waydroid
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
@@ -324,13 +339,18 @@ widevine: Add support for widevine DRM L3
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')
**arg_template, choices=[*remove_choices, * hack_choices], help='Name of app to remove')
remove_parser.set_defaults(func=remove_app)
# hack and its aliases
+32
View File
@@ -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 -1
View File
@@ -4,7 +4,7 @@ from tools.logger import Logger
class AndroidId:
def get_id(self):
def get_id(self):
if container.is_running():
try:
queryout = shell(
+71
View File
@@ -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.")
+4 -5
View File
@@ -1,6 +1,5 @@
import os
import shutil
from stuff.general import General
@@ -23,11 +22,10 @@ class FDroidPriv(General):
"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",
"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]
@@ -41,3 +39,4 @@ class FDroidPriv(General):
if not os.path.exists(rro_dir):
os.makedirs(rro_dir)
shutil.copyfile(os.path.join(self.extract_to, f), rro_file)
+13 -33
View File
@@ -1,6 +1,5 @@
import os
import shutil
from stuff.general import General
from tools.helper import run
@@ -10,32 +9,16 @@ class Gapps(General):
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"]
"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"]
"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 = ...
@@ -141,8 +124,7 @@ class Gapps(General):
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")])
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(
@@ -163,16 +145,14 @@ class Gapps(General):
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")])
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)
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")
@@ -183,7 +163,7 @@ class Gapps(General):
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))
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}")
+3 -4
View File
@@ -1,12 +1,11 @@
import configparser
import glob
import hashlib
import os
import shutil
import zipfile
from tools import container
import hashlib
from tools.helper import download_file, get_download_dir, host
from tools import container
from tools.logger import Logger
@@ -75,7 +74,7 @@ class General:
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)
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:
+1 -3
View File
@@ -1,6 +1,5 @@
import os
import shutil
from stuff.general import General
@@ -13,9 +12,8 @@ class HideStatusBar(General):
dl_link = ...
act_md5 = ...
files = [
"product/overlay/" + dl_file_name
"product/overlay/"+dl_file_name
]
def __init__(self, android_version="11") -> None:
super().__init__()
self.dl_link = self.dl_links[android_version][0]
+2 -3
View File
@@ -1,7 +1,6 @@
import os
import re
import shutil
from stuff.general import General
from tools.logger import Logger
@@ -11,7 +10,7 @@ class Houdini(General):
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/7e21ea3f63bd89e9e8af54e32da41bd8b65c93a1.zip", "f8cf5db10e5fdb9b77e98e515a9b08c9"]
"13": ["https://github.com/supremegamers/vendor_intel_proprietary_houdini/archive/2f8f088671182e17e67321e098e8411a3972a628.zip", "37fe0899f1e4da7f9a724cca1c1ab1ea"]
}
act_md5 = ...
dl_link = ...
@@ -58,7 +57,7 @@ on property:ro.enable.native.bridge.exec=1
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)
"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):
+35 -17
View File
@@ -1,13 +1,11 @@
import gzip
import os
import re
import shutil
import re
from stuff.general import General
from tools import container
from tools.helper import download_file, get_data_dir
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"
@@ -66,7 +64,28 @@ on property:init.svc.zygote=stopped
Logger.info("Additional setup")
magisk_absolute_dir = os.path.join(self.copy_dir, self.magisk_dir)
data_dir = get_data_dir()
shutil.copytree(magisk_absolute_dir, os.path.join(data_dir, "adb", "magisk"), dirs_exist_ok=True)
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)
@@ -77,17 +96,16 @@ on property:init.svc.zygote=stopped
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)
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)
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",
@@ -100,11 +118,11 @@ on property:init.svc.zygote=stopped
# 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:
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)
initfile.write(self.oringinal_bootanim+self.bootanim_component)
def set_path_perm(self, path):
if "magisk" in path.split("/"):
@@ -125,7 +143,7 @@ on property:init.svc.zygote=stopped
def extra1(self):
self.delete_upper()
self.setup()
# Delete the contents of upperdir
def delete_upper(self):
if container.use_overlayfs():
@@ -133,7 +151,7 @@ on property:init.svc.zygote=stopped
files = [
"system/system/etc/init/bootanim.rc",
"system/system/etc/init/bootanim.rc.gz",
"system/system/etc/init/magisk",
"system/system/etc/init/magisk",
"system/system/addon.d/99-magisk.sh",
"vendor/etc/selinux/precompiled_sepolicy"
]
@@ -144,7 +162,7 @@ on property:init.svc.zygote=stopped
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()
+6 -8
View File
@@ -1,6 +1,5 @@
import os
import shutil
from stuff.general import General
from tools.logger import Logger
@@ -16,7 +15,7 @@ class MicroG(General):
"org.microg.nlp.backend.ichnaea_20036.apk": "0b3cb65f8458d1a5802737c7392df903",
"org.microg.nlp.backend.nominatim_20042.apk": "88e7397cbb9e5c71c8687d3681a23383",
}
microg_apks = {
microg_apks= {
"com.google.android.gms-223616054.apk": "a945481ca5d33a03bc0f9418263c3228",
"com.google.android.gsf-8.apk": "b2b4ea3642df6158e14689a4b2a246d4",
"com.android.vending-22.apk": "6815d191433ffcd8fa65923d5b0b0573",
@@ -37,11 +36,11 @@ class MicroG(General):
"6136b383153c2a6797d14fb4d7ca3f97"
],
"Minimal": [
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-Minimal-2.11.1-20230429100521.zip"
"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"
"https://github.com/ayasa520/MinMicroG/releases/download/latest/MinMicroG-MinimalIAP-2.11.1-20230429100556.zip",
"cc071f4f776cbc16c4c1f707aff1f7fa"
]
}
@@ -108,7 +107,7 @@ service microg_service /system/bin/sh /system/bin/npem
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.id = self.id+f"-{variant}"
self.dl_file_name = f'MinMicroG-{variant}.zip'
if android_version == "11":
self.sdk = 30
@@ -124,7 +123,7 @@ service microg_service /system/bin/sh /system/bin/npem
else:
sub_arch = "x86"
if 64 == self.arch[1]:
arch = f"{sub_arch}{'' if sub_arch == 'arm' else '_'}64"
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)
@@ -136,8 +135,7 @@ service microg_service /system/bin/sh /system/bin/npem
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:
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
+1 -3
View File
@@ -1,16 +1,14 @@
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:
def __init__(self, ca_cert_file: str=None) -> None:
super().__init__()
self.ca_cert_file = ca_cert_file
+13 -15
View File
@@ -1,11 +1,9 @@
import os
import re
import shutil
from stuff.general import General
from tools.logger import Logger
class Ndk(General):
id = "libndk"
partition = "system"
@@ -28,19 +26,19 @@ class Ndk(General):
"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*"
]
"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__()
+20 -13
View File
@@ -1,29 +1,32 @@
import os
import re
import shutil
from stuff.general import General
from tools import container
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": {}
"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 = ...
act_md5 = ...
dl_link = None
act_md5 = None
partition = "system"
files = [
"etc/nodataperm.sh",
@@ -37,11 +40,15 @@ class Nodataperm(General):
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("([a-zA-Z0-9]+)\.zip", self.dl_link)[0]
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():
+10 -13
View File
@@ -1,16 +1,14 @@
import os
import shutil
from stuff.general import General
class Smartdock(General):
id = "smartdock"
dl_link = "https://f-droid.org/repo/cu.axel.smartdock_1100.apk"
dl_link = "https://f-droid.org/repo/cu.axel.smartdock_1130.apk"
partition = "system"
dl_file_name = "smartdock.apk"
act_md5 = "f4087d34218eac902a5cca98ee03d215"
apply_props = {"qemu.hw.mainkeys": "1"}
act_md5 = "6bfedb959ef5855c3782e8001cb67f86"
apply_props = { "qemu.hw.mainkeys" : "1" }
skip_extract = True
permissions = """<?xml version="1.0" encoding="utf-8"?>
<permissions>
@@ -32,14 +30,15 @@ class Smartdock(General):
<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"
]
"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
@@ -57,10 +56,8 @@ service set_home_activity /system/bin/sh -c "cmd package set-home-activity cu.ax
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:
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")
+2 -4
View File
@@ -1,7 +1,6 @@
import os
import re
import shutil
from stuff.general import General
from tools.logger import Logger
@@ -19,9 +18,8 @@ class Widevine(General):
},
# "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"]
"11": ["https://github.com/supremegamers/vendor_google_proprietary_widevine-prebuilt/archive/a1a19361d36311bee042da8cf4ced798d2c76d98.zip",
"fed6898b5cfd2a908cb134df97802554"]
}
}
dl_file_name = "widevine.zip"
+179
View File
@@ -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())
+4 -7
View File
@@ -1,12 +1,10 @@
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)
@@ -31,7 +29,7 @@ def use_overlayfs():
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":
if cfg["waydroid"]["mount_overlays"]=="True":
return True
return False
@@ -45,12 +43,11 @@ def stop():
# if session:
# DBusContainerService().Stop(False)
# else:
run(["waydroid", "container", "stop"])
run(["waydroid", "container", "stop"])
def is_running():
return "Session:\tRUNNING" in run(["waydroid", "status"]).stdout.decode()
return "Session:\tRUNNING" in run(["waydroid", "status"]).stdout.decode()
def upgrade():
run(["waydroid", "upgrade", "-o"], ignore=r"\[.*\] Stopping container\n\[.*\] Starting container")
run(["waydroid", "upgrade", "-o"], ignore=r"\[.*\] Stopping container\n\[.*\] Starting container")
+13 -23
View File
@@ -1,16 +1,15 @@
import gzip
import hashlib
import os
import re
import platform
import re
import subprocess
import sys
from typing import Optional
import requests
from tqdm import tqdm
from tools.logger import Logger
from tqdm import tqdm
import hashlib
from typing import Optional
def get_download_dir():
@@ -18,7 +17,7 @@ def get_download_dir():
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"
@@ -27,19 +26,16 @@ def get_download_dir():
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")
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,
args=args,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
@@ -56,7 +52,6 @@ def run(args: list, env: Optional[str] = None, ignore: Optional[str] = None):
)
return result
# execute on waydroid shell
def shell(arg: str, env: Optional[str] = None):
a = subprocess.Popen(
@@ -66,8 +61,7 @@ def shell(arg: str, env: Optional[str] = None):
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"],
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()
@@ -95,7 +89,6 @@ def shell(arg: str, env: Optional[str] = None):
)
return a.stdout.read().decode("utf-8")
def download_file(url, f_name):
md5 = ""
response = requests.get(url, stream=True)
@@ -114,7 +107,6 @@ def download_file(url, f_name):
raise ValueError("Something went wrong while downloading")
return md5
def host():
machine = platform.machine()
@@ -133,7 +125,7 @@ def host():
return ("x86", 32)
return mapping[machine]
raise ValueError("platform.machine '" + machine + "'"
" architecture is not supported")
" architecture is not supported")
def check_root():
@@ -141,16 +133,14 @@ def check_root():
Logger.error("This script must be run as root. Aborting.")
sys.exit(1)
def backup(path):
gz_filename = path + ".gz"
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"
gz_filename = path+".gz"
with gzip.GzipFile(gz_filename) as f_gz:
with open(path, "wb") as f:
f.writelines(f_gz)
+2 -6
View File
@@ -1,18 +1,16 @@
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:
@@ -25,12 +23,10 @@ def umount(mount_point, exists=True):
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()
@@ -40,6 +36,6 @@ def get_image_dir():
sys.exit(1)
cfg.read(cfg_file)
if "waydroid" not in cfg:
Logger.error("Required entry in config was not found, Cannot continue!") # magisk
Logger.error("Required entry in config was not found, Cannot continue!") #magisk
sys.exit(1)
return cfg["waydroid"]["images_path"]
+3 -3
View File
@@ -2,12 +2,12 @@ class Logger:
@staticmethod
def error(str):
print("\033[31m" + "ERROR: " + str + "\033[0m")
print("\033[31m"+"ERROR: "+str+"\033[0m")
@staticmethod
def info(str):
print("\033[32m" + "INFO: " + "\033[0m" + str)
print("\033[32m"+"INFO: "+"\033[0m"+str)
@staticmethod
def warning(str):
print("\033[33m" + "WARN: " + str + "\033[0m")
print("\033[33m"+"WARN: "+str+"\033[0m")