Compare commits

...
Author SHA1 Message Date
JonasAlv 22f03a94c4 Merge branch 'casualsnek:main' into main 2026-08-22 15:24:54 -03:00
ayasa520 d5289cfd89 Update libhoudini (#256)
* Update download link for Houdini version 13
2026-01-05 09:56:03 +01:00
Jonas Alves 1690a5fcd5 fix errors- manual patching 2025-12-19 18:51:05 -03: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
9 changed files with 183 additions and 122 deletions
+4 -22
View File
@@ -1,22 +1,4 @@
with (import <nixpkgs> {}); let
pkgs = import <nixpkgs> {};
stdenv.mkDerivation { in
name = "waydroid_script"; pkgs.callPackage ./package.nix { }
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
'';
}
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;
});
};
}
+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
@@ -10,7 +10,7 @@ class Houdini(General):
partition = "system" partition = "system"
dl_links = { dl_links = {
"11": ["https://github.com/supremegamers/vendor_intel_proprietary_houdini/archive/81f2a51ef539a35aead396ab7fce2adf89f46e88.zip", "fbff756612b4144797fbc99eadcb6653"], "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/9e77896350caccd228b36b2e1b4a994aa4bd48da.zip", "3807fe029559db3037efe245d9e74270"]
} }
act_md5 = ... act_md5 = ...
dl_link = ... dl_link = ...
+14 -6
View File
@@ -10,19 +10,23 @@ from tools import container
class Nodataperm(General): class Nodataperm(General):
id = "nodataperm" id = "nodataperm"
dl_links = { dl_links = {
"11": "11": {
{ "x86_64": [
"https://github.com/ayasa520/hack_full_data_permission/archive/d4beab7780eb792059d33e77d865579c9ee41546.zip",
"b0e3908ffcf5df8ea62f4929aa680f1a"
],
},
"13": {
"x86_64": [ "x86_64": [
"https://github.com/ayasa520/hack_full_data_permission/archive/d4beab7780eb792059d33e77d865579c9ee41546.zip", "https://github.com/ayasa520/hack_full_data_permission/archive/d4beab7780eb792059d33e77d865579c9ee41546.zip",
"b0e3908ffcf5df8ea62f4929aa680f1a" "b0e3908ffcf5df8ea62f4929aa680f1a"
], ],
}, },
"13": {}
} }
dl_file_name = "nodataperm.zip" dl_file_name = "nodataperm.zip"
extract_to = "/tmp/nodataperm" extract_to = "/tmp/nodataperm"
dl_link = ... dl_link = None
act_md5 = ... act_md5 = None
partition = "system" partition = "system"
files = [ files = [
"etc/nodataperm.sh", "etc/nodataperm.sh",
@@ -36,11 +40,15 @@ class Nodataperm(General):
super().__init__() super().__init__()
print("ok") print("ok")
arch = self.arch[0] 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.dl_link = self.dl_links[android_version][arch][0]
self.act_md5 = self.dl_links[android_version][arch][1] self.act_md5 = self.dl_links[android_version][arch][1]
def copy(self): 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( extract_path = os.path.join(
self.extract_to, f"hack_full_data_permission-{name}") self.extract_to, f"hack_full_data_permission-{name}")
if not container.use_overlayfs(): if not container.use_overlayfs():
+4 -24
View File
@@ -1,23 +1,9 @@
import configparser import configparser
import os import os
import sys import sys
# import dbus
from tools.helper import run from tools.helper import run
from tools.logger import Logger 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(): def use_overlayfs():
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg") cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg")
@@ -34,20 +20,14 @@ def use_overlayfs():
return False return False
# def get_session():
# return DBusContainerService().GetSession()
def stop(): def stop():
# if use_dbus():
# session = DBusContainerService().GetSession()
# if session:
# DBusContainerService().Stop(False)
# else:
run(["waydroid", "container", "stop"]) run(["waydroid", "container", "stop"])
def is_running(): def is_running():
return "Session:\tRUNNING" in run(["waydroid", "status"]).stdout.decode() return "Session:\tRUNNING" in run(["waydroid", "status"]).stdout.decode()
def upgrade(): def upgrade():
run(["waydroid", "upgrade", "-o"], ignore=r"\[.*\] Stopping container\n\[.*\] Starting container") try:
run(["waydroid", "upgrade"], ignore=r"\[.*\] Stopping container\n\[.*\] Starting container")
except subprocess.CalledProcessError:
Logger.warning("Waydroid upgrade reported an error (likely system_ota), but we are continuing...")
+64 -66
View File
@@ -2,7 +2,6 @@ import gzip
import os import os
import re import re
import platform import platform
import re
import subprocess import subprocess
import sys import sys
import requests import requests
@@ -11,27 +10,24 @@ from tqdm import tqdm
import hashlib import hashlib
from typing import Optional from typing import Optional
def get_download_dir(): def get_download_dir():
download_loc = "" if os.environ.get("XDG_CACHE_HOME"):
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( download_loc = os.path.join(
os.environ["XDG_CACHE_HOME"], "waydroid-script", "downloads" os.environ["XDG_CACHE_HOME"], "waydroid-script", "downloads"
) )
else:
user = os.environ.get("SUDO_USER", os.environ.get("USER", "root"))
download_loc = os.path.join("/", "home", user, ".cache", "waydroid-script", "downloads")
if not os.path.exists(download_loc): if not os.path.exists(download_loc):
os.makedirs(download_loc) os.makedirs(download_loc, exist_ok=True)
return download_loc return download_loc
# not good
def get_data_dir(): def get_data_dir():
return os.path.join('/', "home", os.environ.get("SUDO_USER", os.environ["USER"]), ".local", "share", "waydroid", "data") user = os.environ.get("SUDO_USER", os.environ.get("USER", "root"))
return os.path.join("/", "home", user, ".local", "share", "waydroid", "data")
# execute on host def run(args: list, env: Optional[dict] = None, ignore: Optional[str] = None):
def run(args: list, env: Optional[str] = None, ignore: Optional[str] = None):
result = subprocess.run( result = subprocess.run(
args=args, args=args,
env=env, env=env,
@@ -39,11 +35,12 @@ def run(args: list, env: Optional[str] = None, ignore: Optional[str] = None):
stderr=subprocess.PIPE stderr=subprocess.PIPE
) )
# print(result.stdout.decode())
if result.stderr: if result.stderr:
error = result.stderr.decode("utf-8") error = result.stderr.decode("utf-8").strip()
if ignore and re.match(ignore, error):
if (ignore and re.search(ignore, error)) or "system_ota" in error:
return result return result
Logger.error(error) Logger.error(error)
raise subprocess.CalledProcessError( raise subprocess.CalledProcessError(
returncode=result.returncode, returncode=result.returncode,
@@ -52,64 +49,58 @@ def run(args: list, env: Optional[str] = None, ignore: Optional[str] = None):
) )
return result return result
# execute on waydroid shell
def shell(arg: str, env: Optional[str] = None): def shell(arg: str, env: Optional[str] = None):
a = subprocess.Popen( cmd = ["sudo", "waydroid", "shell"]
args=["sudo", "waydroid", "shell"], process = subprocess.Popen(
args=cmd,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE stderr=subprocess.PIPE,
text=True
) )
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()
boot_classpath = "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"
full_script = f"{boot_classpath}\n"
if env: if env:
subprocess.Popen( full_script += f"{env}\n"
args=["echo", env], full_script += f"{arg}\n"
stdout=a.stdin,
stdin=subprocess.PIPE
).communicate()
subprocess.Popen( stdout, stderr = process.communicate(input=full_script)
args=["echo", arg],
stdout=a.stdin,
stdin=subprocess.PIPE
).communicate()
a.stdin.close() if process.returncode != 0 and stderr:
if a.stderr.read(): Logger.error(stderr.strip())
Logger.error(a.stderr.read().decode('utf-8'))
raise subprocess.CalledProcessError( raise subprocess.CalledProcessError(
returncode=a.returncode, returncode=process.returncode,
cmd=a.args, cmd=cmd,
stderr=a.stderr stderr=stderr.encode()
) )
return a.stdout.read().decode("utf-8") return stdout
def download_file(url, f_name): def download_file(url, f_name):
md5 = ""
response = requests.get(url, stream=True) response = requests.get(url, stream=True)
total_size_in_bytes = int(response.headers.get('content-length', 0)) total_size_in_bytes = int(response.headers.get('content-length', 0))
block_size = 1024 # 1 Kibibyte block_size = 1024
progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True) progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True)
with open(f_name, 'wb') as file: with open(f_name, 'wb') as file:
for data in response.iter_content(block_size): for data in response.iter_content(block_size):
progress_bar.update(len(data)) progress_bar.update(len(data))
file.write(data) file.write(data)
progress_bar.close() progress_bar.close()
hash_md5 = hashlib.md5()
with open(f_name, "rb") as f: with open(f_name, "rb") as f:
bytes = f.read() for chunk in iter(lambda: f.read(4096), b""):
md5 = hashlib.md5(bytes).hexdigest() hash_md5.update(chunk)
if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes: if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes:
raise ValueError("Something went wrong while downloading") raise ValueError("Download incomplete or corrupted")
return md5
return hash_md5.hexdigest()
def host(): def host():
machine = platform.machine() machine = platform.machine()
mapping = { mapping = {
"i686": ("x86", 32), "i686": ("x86", 32),
"x86_64": ("x86_64", 64), "x86_64": ("x86_64", 64),
@@ -117,30 +108,37 @@ def host():
"armv7l": ("armeabi-v7a", 32), "armv7l": ("armeabi-v7a", 32),
"armv8l": ("armeabi-v7a", 32) "armv8l": ("armeabi-v7a", 32)
} }
if machine in mapping: if machine in mapping:
if mapping[machine] == "x86_64": if machine == "x86_64":
with open("/proc/cpuinfo") as f: try:
if "sse4_2" not in f.read(): with open("/proc/cpuinfo", "r") as f:
Logger.warning("x86_64 CPU does not support SSE4.2, falling back to x86...") if "sse4_2" not in f.read():
return ("x86", 32) Logger.warning("CPU does not support SSE4.2, falling back to x86...")
return ("x86", 32)
except Exception:
pass
return mapping[machine] return mapping[machine]
raise ValueError("platform.machine '" + machine + "'"
" architecture is not supported") raise ValueError(f"Architecture '{machine}' is not supported")
def check_root(): def check_root():
if os.geteuid() != 0: if os.geteuid() != 0:
Logger.error("This script must be run as root. Aborting.") Logger.error("This script must be run as root (sudo). Aborting.")
sys.exit(1) sys.exit(1)
def backup(path): def backup(path):
gz_filename = path+".gz" if not os.path.exists(path):
with gzip.open(gz_filename, 'wb') as f_gz: return
with open(path, "rb") as f: gz_filename = path + ".gz"
f_gz.write(f.read()) with open(path, "rb") as f_in:
with gzip.open(gz_filename, "wb") as f_out:
f_out.writelines(f_in)
def restore(path): def restore(path):
gz_filename = path+".gz" gz_filename = path + ".gz"
with gzip.GzipFile(gz_filename) as f_gz: if not os.path.exists(gz_filename):
with open(path, "wb") as f: return
f.writelines(f_gz) with gzip.open(gz_filename, "rb") as f_in:
with open(path, "wb") as f_out:
f_out.write(f_in.read())
+2 -3
View File
@@ -28,7 +28,6 @@ def resize(img_file, size):
run(["sudo", "resize2fs", img_file, size], ignore="^resize2fs \d+\.\d+\.\d (.+)\n$") run(["sudo", "resize2fs", img_file, size], ignore="^resize2fs \d+\.\d+\.\d (.+)\n$")
def get_image_dir(): def get_image_dir():
# Read waydroid config to get image location
cfg = configparser.ConfigParser() cfg = configparser.ConfigParser()
cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg") cfg_file = os.environ.get("WAYDROID_CONFIG", "/var/lib/waydroid/waydroid.cfg")
if not os.path.isfile(cfg_file): if not os.path.isfile(cfg_file):
@@ -36,6 +35,6 @@ def get_image_dir():
sys.exit(1) sys.exit(1)
cfg.read(cfg_file) cfg.read(cfg_file)
if "waydroid" not in cfg: 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!")
sys.exit(1) sys.exit(1)
return cfg["waydroid"]["images_path"] return cfg["waydroid"]["images_path"]