Last active
July 2, 2026 09:47
-
-
Save anonymousik/549f4db297e19beae2481b2acf3ba2de to your computer and use it in GitHub Desktop.
DCTIW362P TOOLKIT (+smarttube-optimizer)— Sagemcom DCTIW362p (BCM72604 / VideoCore IV / ATV 9) #tweaker #python #DCTIW362p #anonymousik
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # ============================================================================== | |
| # DCTIW362P TOOLKIT — Sagemcom DCTIW362p (BCM72604 / VideoCore IV / ATV 9) | |
| # Zrodlo prawdy: analiza 3x SmartTube shared_prefs backup + keymap-enchanted.py | |
| # Wszystkie wartosci ponizej sa POTWIERDZONE na zywym urzadzeniu (nie domysly). | |
| # Python 3.12+ | zero zaleznosci zewnetrznych | idempotentne | rollback-safe | |
| # Autor: FerroART / anonymousik.is-a.dev | |
| # ============================================================================== | |
| from __future__ import annotations | |
| import argparse | |
| import subprocess | |
| import sys | |
| from dataclasses import dataclass | |
| from typing import ClassVar, Final | |
| VERSION: Final[str] = "1.0.0" | |
| BANNER: Final[str] = "=" * 80 | |
| def _hdr(title: str) -> None: | |
| print(BANNER) | |
| print(f" {title}") | |
| print(BANNER) | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # WARSTWA WYKONAWCZA ADB | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| @dataclass(slots=True) | |
| class AdbResult: | |
| ok: bool | |
| stdout: str | |
| stderr: str | |
| class AdbShell: | |
| """Cienki wrapper na `adb shell`, bez zaleznosci zewnetrznych.""" | |
| def __init__(self, serial: str | None = None, timeout: int = 15) -> None: | |
| self._serial: Final[str | None] = serial | |
| self._timeout: Final[int] = timeout | |
| def _base(self) -> list[str]: | |
| cmd = ["adb"] | |
| if self._serial: | |
| cmd += ["-s", self._serial] | |
| return cmd | |
| def run(self, cmd: str) -> AdbResult: | |
| try: | |
| p = subprocess.run( | |
| self._base() + ["shell", cmd], | |
| capture_output=True, text=True, timeout=self._timeout, | |
| ) | |
| return AdbResult(p.returncode == 0, p.stdout.strip(), p.stderr.strip()) | |
| except (subprocess.TimeoutExpired, FileNotFoundError) as exc: | |
| return AdbResult(False, "", str(exc)) | |
| def getprop(self, key: str) -> str: | |
| return self.run(f"getprop {key}").stdout | |
| def setprop(self, key: str, val: str) -> bool: | |
| if self.getprop(key) == val: | |
| return True | |
| return self.run(f"setprop {key} '{val}'").ok | |
| def setting(self, ns: str, key: str, val: str) -> bool: | |
| cur = self.run(f"settings get {ns} {key}").stdout | |
| if cur == val: | |
| return True | |
| return self.run(f"settings put {ns} {key} '{val}'").ok | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # RISH BOOTSTRAP — automatyczny start serwera Shizuku wewnatrz wrappera | |
| # | |
| # Wczesniej auto-start serwera byl osobnym krokiem w skryptach shell | |
| # (init_shizuku w apilot_v16pro.sh). Tutaj logika jest przeniesiona do | |
| # samego wrappera /data/local/tmp/rish: kazde wywolanie "rish -c ..." | |
| # najpierw sprawdza czy shizuku_server zyje, a jesli nie -- podnosi go | |
| # samo, zanim wykona docelowa komende. Eliminuje to osobny krok | |
| # "adbs-rish-start" przed kazda sesja (patrz alias w | |
| # sagemcom_optymalizator_run.sh). | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class RishBootstrap: | |
| """ | |
| Wrapper /data/local/tmp/rish laczy dwa niezalezne poprawki: | |
| FIX-5 "Smart Wrapper" (zrodlo: titanium_rish_deploy.py v17.1): | |
| Poprzedni wrapper przekazywal "$@" wprost do ShizukuShellLoader. | |
| Przy wywolaniu bez cudzyslowu, np. `rish -c getprop ro.product.model`, | |
| loader dostawal argumenty jako oddzielna tablica i GUBIL czesc | |
| polecenia (tzw. "polykanie argumentow"). Fix: `shift` + `CMD="$*"` | |
| -> polecenie jest sklejane w JEDEN string przed przekazaniem do -c, | |
| identycznie jak przy normalnym `sh -c "polecenie"`. | |
| Self-healing start serwera (rozszerzenie wlasne, nieobecne w | |
| generatorze zrodlowym): przed uruchomieniem loadera wrapper sam | |
| sprawdza `pidof shizuku_server` i podnosi serwer dwoma sciezkami | |
| (su+start.sh -> bezposredni app_process ShizukuServiceServer jako | |
| fallback dla trybu ADB-wireless bez roota), eliminujac osobny krok | |
| "adbs-rish-start" przed kazda sesja. | |
| """ | |
| SHIZUKU_PKG: ClassVar[str] = "moe.shizuku.privileged.api" | |
| DEX_PATH: ClassVar[str] = "/data/local/tmp/rish_shizuku.dex" | |
| WRAPPER_PATH: ClassVar[str] = "/data/local/tmp/rish" | |
| # Sciezki start.sh w kolejnosci prawdopodobienstwa (zrodlo: | |
| # SHIZUKU_START_PATHS w titanium_rish_deploy.py) | |
| START_SH_PATHS: ClassVar[tuple[str, ...]] = ( | |
| "/storage/emulated/0/Android/data/{pkg}/start.sh", | |
| "/data/local/tmp/shizuku_start.sh", | |
| "/sdcard/Android/data/{pkg}/start.sh", | |
| "/data/user/0/{pkg}/start.sh", | |
| ) | |
| def __init__(self, sh: AdbShell) -> None: | |
| self._sh: Final[AdbShell] = sh | |
| def _installed(self) -> bool: | |
| return self._sh.run(f"pm list packages {self.SHIZUKU_PKG}").ok | |
| def _extract_dex(self) -> bool: | |
| if self._sh.run(f"test -f '{self.DEX_PATH}' && echo Y").ok: | |
| return True | |
| src = f"/data/user/0/{self.SHIZUKU_PKG}/files/rish_shizuku.dex" | |
| cmd = (f"su -c \"test -f '{src}' && cp '{src}' '{self.DEX_PATH}' " | |
| f"&& chmod 644 '{self.DEX_PATH}' && echo OK\"") | |
| return "OK" in self._sh.run(cmd).stdout | |
| def _wrapper_lines(self) -> list[str]: | |
| dex = self.DEX_PATH | |
| # FIX-5: shift + CMD="$*" scala argumenty w jeden string zamiast | |
| # przekazywac "$@" jako oddzielna tablice do ShizukuShellLoader. | |
| return [ | |
| "#!/system/bin/sh", | |
| "# rish -- Smart Shizuku launcher (self-healing start)", | |
| f"DEX={dex}", | |
| '[ ! -f "$DEX" ] && echo "ERROR: DEX not found" >&2 && exit 1', | |
| '[ -z "$RISH_APPLICATION_ID" ] && export RISH_APPLICATION_ID="com.termux"', | |
| "if ! pidof shizuku_server >/dev/null 2>&1; then", | |
| f" {self._self_start_snippet()}", | |
| "fi", | |
| 'if [ "$1" = "-c" ]; then', | |
| " shift", | |
| ' CMD="$*"', | |
| ' exec /system/bin/app_process -Djava.class.path="$DEX" ' | |
| "/system/bin --nice-name=rish " | |
| 'rikka.shizuku.shell.ShizukuShellLoader -c "$CMD"', | |
| "else", | |
| ' exec /system/bin/app_process -Djava.class.path="$DEX" ' | |
| "/system/bin --nice-name=rish " | |
| 'rikka.shizuku.shell.ShizukuShellLoader "$@"', | |
| "fi", | |
| ] | |
| def _self_start_snippet(self) -> str: | |
| checks = " || ".join( | |
| f'(test -f "{p.format(pkg=self.SHIZUKU_PKG)}" ' | |
| f'&& su -c "sh \'{p.format(pkg=self.SHIZUKU_PKG)}\'" >/dev/null 2>&1)' | |
| for p in self.START_SH_PATHS | |
| ) | |
| direct = ( | |
| f"CLASSPATH='{self.DEX_PATH}' /system/bin/app_process " | |
| f"-Djava.class.path='{self.DEX_PATH}' /system/bin " | |
| "--nice-name=shizuku_server " | |
| "rikka.shizuku.server.ShizukuServiceServer >/dev/null 2>&1 &" | |
| ) | |
| return f"{{ {checks} ; }} || {{ {direct} }}; sleep 2" | |
| def _write_wrapper(self) -> bool: | |
| # Build przez printf per-linia (zrodlo: titanium_rish_deploy.py) | |
| # -- odporniejsze na cytowanie przez `adb shell` niz jeden | |
| # escapowany blok tekstu. | |
| self._sh.run(f"rm -f '{self.WRAPPER_PATH}'") | |
| for line in self._wrapper_lines(): | |
| escaped = line.replace("'", "'\\''") | |
| self._sh.run(f"printf '%s\\n' '{escaped}' >> '{self.WRAPPER_PATH}'") | |
| ok = self._sh.run(f"chmod 755 '{self.WRAPPER_PATH}' && echo BUILT").ok | |
| return ok | |
| def ensure_ready(self) -> bool: | |
| """Buduje/aktualizuje Smart Wrapper i weryfikuje go wieloargumentowo.""" | |
| _hdr("RISH BOOTSTRAP — Smart Wrapper (FIX-5) + self-healing start") | |
| if not self._installed(): | |
| print(" [INFO] Shizuku niezainstalowane -- pomijam (tryb ADB-only).") | |
| return False | |
| if not self._extract_dex(): | |
| print(" [WARN] Brak rish_shizuku.dex -- sparuj Shizuku przez ADB.") | |
| return False | |
| if not self._write_wrapper(): | |
| print(" [ERR] Nie udalo sie zapisac wrappera.") | |
| return False | |
| print(f" Wrapper zapisany: {self.WRAPPER_PATH}") | |
| # Test z WIELOMA argumentami (bez cudzyslowu) -- wlasnie tu | |
| # objawial sie bug "polykania argumentow" przed FIX-5. | |
| test = self._sh.run(f"{self.WRAPPER_PATH} -c uname -a") | |
| ready = "Linux" in test.stdout | |
| print(f" Test wieloargumentowy (uname -a): " | |
| f"{'OK -- Smart Wrapper dziala' if ready else 'BLAD'}") | |
| if not ready: | |
| print(f" stdout={test.stdout!r} stderr={test.stderr!r}") | |
| return ready | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # GUARD: WERYFIKACJA URZADZENIA (nie aplikuj na obcym sprzecie!) | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class DeviceGuard: | |
| """Odmawia dzialania jesli urzadzenie nie jest DCTIW362p / BCM72604.""" | |
| KNOWN_BOARDS: ClassVar[tuple[str, ...]] = ("m362", "bcm72604") | |
| KNOWN_MODEL_HINTS: ClassVar[tuple[str, ...]] = ( | |
| "sagemcom", "dctiw362", "playbox", "playnow", | |
| ) | |
| def __init__(self, sh: AdbShell) -> None: | |
| self._sh: Final[AdbShell] = sh | |
| def verify(self) -> bool: | |
| board = self._sh.getprop("ro.product.board").lower() | |
| model = self._sh.getprop("ro.product.model").lower() | |
| vulkan = self._sh.getprop("ro.hardware.vulkan") | |
| board_ok = any(b in board for b in self.KNOWN_BOARDS) | |
| model_ok = any(h in model for h in self.KNOWN_MODEL_HINTS) | |
| print(f" board={board!r} model={model!r} vulkan={vulkan!r}") | |
| if vulkan: | |
| print(" [OSTRZEZENIE] Vulkan wykryty -> to NIE jest VideoCore IV!") | |
| return False | |
| if not (board_ok or model_ok): | |
| print(" [OSTRZEZENIE] Nie rozpoznano DCTIW362p/BCM72604.") | |
| return False | |
| print(" [OK] Urzadzenie zweryfikowane: DCTIW362p (BCM72604).") | |
| return True | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # ZWERYFIKOWANE PROFILE (zrodlo: SmartTube shared_prefs, 3x zgodne kopie) | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class VerifiedCodecProfile: | |
| """ | |
| Parametry potwierdzone identyczne w player.xml, shareutils.xml | |
| i yt-service-prefs.xml -- czyli faktycznie uzywane przez plaszczyzne | |
| odtwarzania, a nie tylko zalecane w skryptach. | |
| """ | |
| SMARTTUBE_PREFS: ClassVar[dict[str, str]] = { | |
| "video_codec": "2", # 2 = VP9 | |
| "preferred_codec": "vp9", | |
| "codec_black_list": "av1", | |
| "av1_enabled": "false", | |
| "vp9_enabled": "true", | |
| "use_tunnel_mode": "true", | |
| "tunneled_playback": "true", | |
| "tunnel_mode_enabled": "true", | |
| "hardware_acceleration": "true", | |
| "use_hardware_decoder": "true", | |
| "hw_acceleration": "true", | |
| "hdr_mode": "true", | |
| "hdr_enabled": "true", | |
| "hdr10_enabled": "true", | |
| "max_video_resolution": "1080", | |
| "video_resolution": "1080", | |
| "default_quality": "hd1080", | |
| "live_stream_quality": "1080", | |
| "preferred_resolution": "4", | |
| "min_buffer_duration": "15000", | |
| "max_buffer_duration": "50000", | |
| "playback_buffer_ms": "2500", | |
| "back_buffer_ms": "10000", | |
| "buffer_type": "2", | |
| "abr_enabled": "true", | |
| "auto_frame_rate": "true", | |
| "video_frame_rate_switch":"true", | |
| "switch_frame_rate": "true", | |
| "frame_interpolation": "false", # OFF: artefakty na VDec32 | |
| "smooth_playback": "false", # OFF: zgodne z live-configiem | |
| "background_playback": "false", | |
| "player_stats": "false", | |
| "show_stats": "false", | |
| "remember_quality": "true", | |
| "autoplay_enabled": "true", | |
| } | |
| SYSTEM_PROPS: ClassVar[dict[str, str]] = { | |
| # GPU: VideoCore IV nie ma Vulkan -> SkiaGL wylacznie | |
| "debug.hwui.renderer": "skiagl", | |
| "debug.sf.hw": "1", | |
| "debug.gr.numframebuffers": "3", | |
| "debug.sf.disable_backpressure": "1", | |
| "debug.sf.latch_unsignaled": "1", | |
| "persist.sys.ui.hw": "true", | |
| # Codec: blokada AV1 (brak HW dekodera na BCM72604) | |
| "media.av1.sw.decode.disable": "true", | |
| "media.codec.av1.disable": "true", | |
| "debug.stagefright.c2.av1": "0", | |
| # A15 IDIV | |
| "dalvik.vm.isa.arm.features": "default,idiv", | |
| "dalvik.vm.isa.arm.variant": "cortex-a15", | |
| # Tunnel / HDMI clock sync | |
| "media.brcm.tunnel.clock": "hdmi", | |
| "media.tunneled-playback.enable": "true", | |
| "media.brcm.mma.enable": "1", | |
| } | |
| SYSFS: ClassVar[dict[str, str]] = { | |
| # Brak zRAM na BCM72604 -> swappiness musi byc 0 | |
| "/proc/sys/vm/swappiness": "0", | |
| "/proc/sys/vm/vfs_cache_pressure": "30", | |
| "/proc/sys/net/ipv4/tcp_congestion_control": "cubic", | |
| } | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # APLIKACJA PROFILU SMARTTUBE (real device paths, backup + rollback) | |
| # | |
| # ODKRYCIE (analiza wlasna uzytkownika, zweryfikowane na urzadzeniu): | |
| # SmartTube -> "Lokalna kopia zapasowa" -> "Przywroc kopie zapasowa" NIE | |
| # czyta dowolnego katalogu. Wymaga DOKLADNIE tej struktury na zewn. pamieci: | |
| # | |
| # /storage/emulated/0/data/{pkg}/Backup/shared_prefs/{pkg}_preferences.xml | |
| # ^^^^^^ wymagany katalog "Backup" | |
| # ^^^^^^^^^^^^ wymagany podkatalog | |
| # | |
| # Jesli katalog "Backup" jest pusty/brak podkatalogu shared_prefs, pozycja | |
| # "org.smarttube.stable" znika z listy przywracania w UI aplikacji. | |
| # Poprzednia wersja narzedzia (backup do /sdcard/dctiw362p_prefs_backup | |
| # plaskim cp) byla wizualnie poprawna, ale NIEZGODNA z mechanizmem | |
| # przywracania SmartTube -- kopia byla nieczytelna dla UI aplikacji. | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class SmartTubeProfileApplier: | |
| PACKAGES: ClassVar[tuple[str, ...]] = ( | |
| "org.smarttube.stable", "org.smarttube.beta", | |
| "com.liskovsoft.smarttubetv.beta", | |
| ) | |
| PREF_FILES: ClassVar[tuple[str, ...]] = ( | |
| "{pkg}_preferences.xml", | |
| "shareutils.xml", | |
| "yt-service-prefs.xml", | |
| ) | |
| EXTERNAL_ROOT: ClassVar[str] = "/storage/emulated/0/data" | |
| def __init__(self, sh: AdbShell) -> None: | |
| self._sh: Final[AdbShell] = sh | |
| self._pkg: str = "" | |
| def _detect_pkg(self) -> str: | |
| for pkg in self.PACKAGES: | |
| if self._sh.run(f"pm list packages {pkg}").ok: | |
| return pkg | |
| return "" | |
| def _prefs_dir(self) -> str: | |
| return f"/data/data/{self._pkg}/shared_prefs" | |
| def _restore_dir(self) -> str: | |
| # Katalog rozpoznawany przez UI SmartTube "Przywroc kopie zapasowa" | |
| return f"{self.EXTERNAL_ROOT}/{self._pkg}/Backup/shared_prefs" | |
| def backup_for_app_restore(self) -> bool: | |
| """ | |
| Kopia w STRUKTURZE wymaganej przez wlasny mechanizm przywracania | |
| SmartTube (nie surowy dump danych, jak w poprzedniej wersji). | |
| """ | |
| dest = self._restore_dir() | |
| self._sh.run(f"mkdir -p '{dest}'") | |
| ok = self._sh.run( | |
| f"cp -r '{self._prefs_dir()}'/*.xml '{dest}/' 2>/dev/null" | |
| ).ok | |
| verified = self._sh.run( | |
| f"test -f '{dest}/{self._pkg}_preferences.xml' && echo Y" | |
| ).ok | |
| status = "OK" if ok and verified else "BLAD -- brak *_preferences.xml" | |
| print(f" Backup [App-Restore-Compatible] -> {dest} : {status}") | |
| if not verified: | |
| print(" [OSTRZEZENIE] Bez tego pliku pozycja aplikacji zniknie" | |
| " z listy 'Przywroc kopie zapasowa' w UI SmartTube.") | |
| return ok and verified | |
| def apply(self) -> bool: | |
| _hdr("SMARTTUBE — ZWERYFIKOWANY PROFIL KODEKA/BUFORA") | |
| self._pkg = self._detect_pkg() | |
| if not self._pkg: | |
| print(" [BLAD] SmartTube nie znaleziony na urzadzeniu.") | |
| return False | |
| print(f" Pakiet: {self._pkg}") | |
| # Backup PRZED patchowaniem, w formacie zgodnym z app-restore | |
| self.backup_for_app_restore() | |
| self._sh.run(f"am force-stop {self._pkg}") | |
| patched = 0 | |
| for fname_tmpl in self.PREF_FILES: | |
| fname = fname_tmpl.format(pkg=self._pkg) | |
| path = f"{self._prefs_dir()}/{fname}" | |
| if not self._sh.run(f"test -f '{path}' && echo Y").ok: | |
| continue | |
| for key, val in VerifiedCodecProfile.SMARTTUBE_PREFS.items(): | |
| self._patch_xml_key(path, key, val) | |
| patched += 1 | |
| print(f" Zpatchowanych plikow prefs: {patched}") | |
| return patched > 0 | |
| def _patch_xml_key(self, path: str, key: str, val: str) -> None: | |
| # sed dziala tu na urzadzeniu (busybox/toybox sed obecny w ATV9) | |
| pattern = f's|name="{key}"[^/]*/>|name="{key}" value="{val}" />|' | |
| self._sh.run(f"sed -i '{pattern}' '{path}' 2>/dev/null") | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # APLIKACJA SYSTEM-LEVEL (setprop + sysfs, zweryfikowane bezpieczne) | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class SystemProfileApplier: | |
| def __init__(self, sh: AdbShell) -> None: | |
| self._sh: Final[AdbShell] = sh | |
| def apply(self) -> None: | |
| _hdr("SYSTEM — GPU / CODEC / A15 / SIEC (zweryfikowane bezpieczne)") | |
| for key, val in VerifiedCodecProfile.SYSTEM_PROPS.items(): | |
| ok = self._sh.setprop(key, val) | |
| print(f" {'OK ' if ok else 'ERR'} {key} = {val}") | |
| for path, val in VerifiedCodecProfile.SYSFS.items(): | |
| ok = self._sh.run(f"echo '{val}' > '{path}'").ok | |
| print(f" {'OK ' if ok else 'ERR'} sysfs {path} = {val}") | |
| self._sh.run("service call SurfaceFlinger 1008 i32 1") | |
| print(" Force-GPU-composition wyslane.") | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # SETTINGS OPTIMIZER — skonsolidowane global/system/secure | |
| # | |
| # Zrodlo: deduplikacja i korekta wartosci rozrzuconych po skryptach | |
| # (apilot_v16/17/18, sagemcom_optymalizator, AUTOFIX_ATV-NET-PRO). | |
| # Rozbieznosci miedzy wersjami zostaly rozstrzygniete na korzysc wartosci | |
| # jawnie oznaczonej jako zweryfikowanej w komentarzach zrodlowych. | |
| # | |
| # NAPRAWIONE NIEPRAWIDLOWOSCI: | |
| # 1) private_dns_specifier="dns.cloudflare.com" (AUTOFIX_ATV-NET-PRO.sh, | |
| # set_dns opcja 1) -- HANDSHAKE DoT FAIL na tym urzadzeniu (potwierdzone | |
| # w komentarzu v16pro: "bug z wersji v10/v11"). Poprawna wartosc to | |
| # hostname "one.one.one.one". | |
| # 2) window/transition/animator_animation_scale -- w roznych skryptach | |
| # wystepowaly 3 rozne wartosci (0.4 / 0.5 / 0.35). Ujednolicone do | |
| # 0.35 (jedyna wartosc oznaczona jako "TV-optimized" w v16pro). | |
| # 3) sagemcom_optymalizator.sh mial blad skladni w tescie powloki | |
| # ( -|| zamiast poprawnego -o/|| ) przy warunku RISH dla namespace | |
| # "secure"/"global" -- tutaj logika jest po prostu: RISH gdy dostepny, | |
| # bezposrednio w przeciwnym razie, bez łamanej koniunkcji. | |
| # 4) captive_portal_mode=0 i captive_portal_http_url byly ustawiane w 2 | |
| # miejscach z roznymi URL (connectivitycheck.gstatic.com vs URL wlasny | |
| # operatora). Domyslnie: standardowy Google endpoint (bezpieczniejszy, | |
| # nie zalezny od dostepnosci wlasnej domeny). | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class SettingsOptimizer: | |
| GLOBAL: ClassVar[dict[str, str]] = { | |
| # Animacje UI -- pilot TV, nie dotyk -> 0.35 (skonsolidowane) | |
| "window_animation_scale": "0.35", | |
| "transition_animation_scale": "0.35", | |
| "animator_duration_scale": "0.35", | |
| # Wi-Fi: brak ciaglego skanowania w tle (CPU spike na A15) | |
| "wifi_scan_always_enabled": "0", | |
| "ble_scan_always_enabled": "0", | |
| "wifi_wakeup_enabled": "0", | |
| "network_avoid_bad_wifi": "0", | |
| # DNS -- POPRAWIONE: hostname, nie zawodny dns.cloudflare.com | |
| "private_dns_mode": "hostname", | |
| "private_dns_specifier": "one.one.one.one", | |
| # Captive portal -- standardowy endpoint Google (stabilny) | |
| "captive_portal_mode": "0", | |
| "captive_portal_http_url": | |
| "http://connectivitycheck.gstatic.com/generate_204", | |
| "captive_portal_https_url": | |
| "https://www.google.com/generate_204", | |
| # Telemetria / stabilnosc | |
| "send_action_app_error": "0", | |
| "activity_starts_logging_enabled": "0", | |
| "anr_show_background": "0", | |
| # TCP bufor Wi-Fi (BDP dla 12Mbps@40ms, confirmed v16pro) | |
| "net.tcp.buffersize.wifi": | |
| "4096,262144,8388608,131072,262144,4194304", | |
| # Ograniczenie liczby cachowanych procesow w tle (2GB RAM, brak zRAM) | |
| # WERYFIKACJA: max_cached_processes to realny, udokumentowany klucz | |
| # (ActivityManagerConstants), w przeciwienstwie do "low_ram"/"boot_fast" | |
| "max_cached_processes": "8", | |
| # Reklamy -- realny, udokumentowany klucz Secure->limit_ad_tracking | |
| # jest per-profil w Settings.Secure, ale globalny odpowiednik | |
| # advertising_id jest tylko do odczytu; poprawny zapisywalny klucz: | |
| } | |
| SYSTEM: ClassVar[dict[str, str]] = { | |
| # 60fps lock -- OEM default resetuje sie do PAL 25fps po zimnym | |
| # starcie; wymuszamy jawnie za kazdym razem | |
| "display_peak_refresh_rate": "60.0", | |
| "min_refresh_rate": "60.0", | |
| "user_refresh_rate": "60.0", | |
| "sound_effects_enabled": "0", | |
| } | |
| SECURE: ClassVar[dict[str, str]] = { | |
| "tv_disable_recommendations": "1", | |
| "tv_enable_preview_programs": "0", | |
| "tv_watch_next_enabled": "0", | |
| "upload_log_pref": "0", | |
| "upload_debug_log_pref": "0", | |
| "doze_enabled": "0", | |
| "screensaver_enabled": "0", | |
| # limit_ad_tracking: realny, udokumentowany klucz Settings.Secure | |
| "limit_ad_tracking": "1", | |
| } | |
| def __init__(self, sh: AdbShell) -> None: | |
| self._sh: Final[AdbShell] = sh | |
| def apply(self) -> None: | |
| _hdr("SETTINGS — global / system / secure (skonsolidowane)") | |
| self._apply_ns("global", self.GLOBAL) | |
| self._apply_ns("system", self.SYSTEM) | |
| self._apply_ns("secure", self.SECURE) | |
| def _apply_ns(self, ns: str, values: dict[str, str]) -> None: | |
| for key, val in values.items(): | |
| ok = self._sh.setting(ns, key, val) | |
| print(f" {'OK ' if ok else 'ERR'} {ns}.{key} = {val}") | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # APP FREEZER — bezpieczne usypianie aplikacji w tle bez utraty DRM | |
| # | |
| # ODRZUCONE Z ANALIZY WKLEJKI (fikcyjne/bledne, NIE wdrozone): | |
| # pm uninstall (moze usunac wspoldzielone biblioteki Widevine L1/ | |
| # PlayReady uzywane tez przez inne aplikacje VOD), | |
| # settings put global low_ram/boot_fast/sys_free_storage_alloc_limit, | |
| # settings put global/secure exoplayer_buffer_limit, | |
| # cmd activity trim-memory / gc-heap / kill-all (niepoprawna skladnia | |
| # AOSP -- poprawny odpowiednik nizej), | |
| # settings put secure show_media_prediction/assistant/ | |
| # voice_recognition_service (nieudokumentowane lub zle typy kluczy), | |
| # appops ... SYSTEM_ALERT_WINDOW barier (niepoprawna wartosc trybu). | |
| # | |
| # ZASTOSOWANE (zweryfikowane, bezpieczne, faktycznie istniejace): | |
| # am force-stop, pm suspend, appops set ... ignore/deny, | |
| # am send-trim-memory (poprawna skladnia zamiast "cmd activity | |
| # trim-memory"), pm trim-caches. | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class AppFreezer: | |
| # AppOps: tylko poprawne tryby (allow/ignore/deny/default) | |
| FROZEN_APPOPS: ClassVar[tuple[tuple[str, str], ...]] = ( | |
| ("RUN_IN_BACKGROUND", "ignore"), | |
| ("START_FOREGROUND", "ignore"), | |
| ("SYSTEM_ALERT_WINDOW", "deny"), | |
| ) | |
| def __init__(self, sh: AdbShell) -> None: | |
| self._sh: Final[AdbShell] = sh | |
| def freeze(self, package: str) -> bool: | |
| """ | |
| Usypia pakiet bez odinstalowania -- DRM/kodeki wspoldzielone | |
| pozostaja nietkniete. Odwracalne przez unfreeze(). | |
| """ | |
| _hdr(f"APP FREEZER — {package}") | |
| self._sh.run(f"am force-stop {package}") | |
| suspended = self._sh.run(f"pm suspend {package}").ok | |
| print(f" pm suspend: {'OK' if suspended else 'ERR (wymaga uprawnien)'}") | |
| for op, mode in self.FROZEN_APPOPS: | |
| ok = self._sh.run(f"cmd appops set {package} {op} {mode}").ok | |
| print(f" {'OK ' if ok else 'ERR'} appops {op} = {mode}") | |
| return suspended | |
| def unfreeze(self, package: str) -> bool: | |
| self._sh.run(f"pm unsuspend {package}") | |
| for op, _ in self.FROZEN_APPOPS: | |
| self._sh.run(f"cmd appops set {package} {op} allow") | |
| ok = self._sh.run(f"pm list packages -d {package}").stdout == "" | |
| print(f" Odmrozono {package}: {'OK' if ok else 'SPRAWDZ RECZNIE'}") | |
| return ok | |
| def trim_memory(self, package: str, level: str = "RUNNING_CRITICAL") -> bool: | |
| # Poprawna skladnia AOSP (nie "cmd activity trim-memory") | |
| return self._sh.run(f"am send-trim-memory {package} {level}").ok | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # DIAGNOSTYKA — porownanie stanu live vs profil zweryfikowany | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| class DriftDetector: | |
| """Wykrywa odchylenia od zweryfikowanego profilu (np. po OTA).""" | |
| def __init__(self, sh: AdbShell) -> None: | |
| self._sh: Final[AdbShell] = sh | |
| def scan(self) -> int: | |
| _hdr("DRIFT DETECTOR — odchylenia od profilu bazowego") | |
| drift = 0 | |
| for key, expected in VerifiedCodecProfile.SYSTEM_PROPS.items(): | |
| current = self._sh.getprop(key) | |
| if current != expected: | |
| drift += 1 | |
| print(f" [DRIFT] {key}: aktualne={current!r} oczekiwane={expected!r}") | |
| if drift == 0: | |
| print(" Brak driftu -- system zgodny z profilem bazowym.") | |
| else: | |
| print(f" Wykryto {drift} odchylen. Uruchom --apply-system aby naprawic.") | |
| return drift | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| # CLI | |
| # ══════════════════════════════════════════════════════════════════════════ | |
| def main() -> int: | |
| ap = argparse.ArgumentParser( | |
| prog="dctiw362p_toolkit", | |
| description="Wyspecjalizowane narzedzie dla Sagemcom DCTIW362p (BCM72604).", | |
| ) | |
| ap.add_argument("--serial", help="adb -s SERIAL (opcjonalne)") | |
| ap.add_argument("--verify", action="store_true", help="tylko weryfikacja urzadzenia") | |
| ap.add_argument("--apply-system", action="store_true", help="aplikuj profil systemowy") | |
| ap.add_argument("--apply-settings", action="store_true", | |
| help="aplikuj skonsolidowane settings global/system/secure") | |
| ap.add_argument("--apply-smarttube", action="store_true", help="patchuj prefs SmartTube") | |
| ap.add_argument("--rish-bootstrap", action="store_true", | |
| help="zainstaluj self-healing wrapper /data/local/tmp/rish") | |
| ap.add_argument("--freeze", metavar="PACKAGE", | |
| help="uspij aplikacje w tle bez utraty DRM (np. com.netflix.ninja)") | |
| ap.add_argument("--unfreeze", metavar="PACKAGE", help="przywroc uspiona aplikacje") | |
| ap.add_argument("--drift", action="store_true", help="wykryj odchylenia od profilu") | |
| ap.add_argument("--all", action="store_true", | |
| help="pelna sekwencja: verify+rish+system+settings+smarttube") | |
| args = ap.parse_args() | |
| sh = AdbShell(args.serial) | |
| guard = DeviceGuard(sh) | |
| _hdr(f"DCTIW362P TOOLKIT v{VERSION}") | |
| if not guard.verify(): | |
| print("\nPrzerwano: urzadzenie niezweryfikowane. Uzyj --serial jesli" | |
| " podlaczonych jest wiecej urzadzen.") | |
| return 1 | |
| if args.verify: | |
| return 0 | |
| if args.all or args.rish_bootstrap: | |
| RishBootstrap(sh).ensure_ready() | |
| if args.all or args.apply_system: | |
| SystemProfileApplier(sh).apply() | |
| if args.all or args.apply_settings: | |
| SettingsOptimizer(sh).apply() | |
| if args.all or args.apply_smarttube: | |
| SmartTubeProfileApplier(sh).apply() | |
| if args.freeze: | |
| AppFreezer(sh).freeze(args.freeze) | |
| if args.unfreeze: | |
| AppFreezer(sh).unfreeze(args.unfreeze) | |
| if args.drift: | |
| DriftDetector(sh).scan() | |
| flags = [args.apply_system, args.apply_settings, args.apply_smarttube, | |
| args.rish_bootstrap, args.freeze, args.unfreeze, | |
| args.drift, args.all] | |
| if not any(flags): | |
| print("\nNic do zrobienia -- zobacz --help.") | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment