From 3bc2b310a3f44f4558878250b79f02b2d0b84ec4 Mon Sep 17 00:00:00 2001 From: mcheeseman Date: Tue, 16 Jun 2026 20:03:41 +0800 Subject: [PATCH] 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 --- main.py | 21 +++++++++++-- stuff/device_spoof.py | 71 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 stuff/device_spoof.py diff --git a/main.py b/main.py index 35bc1fc..3aef557 100755 --- a/main.py +++ b/main.py @@ -18,6 +18,7 @@ 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 @@ -89,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" @@ -140,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: @@ -229,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"]) @@ -251,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": @@ -296,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 @@ -327,6 +339,11 @@ 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 diff --git a/stuff/device_spoof.py b/stuff/device_spoof.py new file mode 100644 index 0000000..ad8adc1 --- /dev/null +++ b/stuff/device_spoof.py @@ -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.")