Created
February 25, 2026 06:02
-
-
Save anonymousik/20d96b80b3487d36084a02ded508aabe to your computer and use it in GitHub Desktop.
fix(v13): hardware-grounded from live getprop — 12 targeted fixes, PSI-LMK, A15-idiv, V3D-fence, MMA, TCP-FO v3 — Hardware-Targeted Edition
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 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| ╔══════════════════════════════════════════════════════════════════════════════╗ | |
| ║ PLAYBOX TITANIUM v13.0 PRECISION — Hardware-Targeted Edition ║ | |
| ║ Target : Sagemcom DCTIW362P | Android TV 9 API 28 | PTT1.190826.001 ║ | |
| ║ Kernel : 4.9.190-1-6pre armv7l ║ | |
| ╠══════════════════════════════════════════════════════════════════════════════╣ | |
| ║ REAL HARDWARE (verified from live getprop dump): ║ | |
| ║ CPU : ARMv7 Cortex-A15 dual-core @ ~1.0 GHz ║ | |
| ║ dalvik.vm.isa.arm.variant = cortex-a15 ║ | |
| ║ dalvik.vm.isa.arm.features = default ← A15 idiv NOT enabled ║ | |
| ║ GPU : Broadcom VideoCore | ro.gfx.driver.0 = gfxdriver-bcmstb ║ | |
| ║ ro.opengles.version = 196609 (GLES 3.1) ║ | |
| ║ ro.v3d.fence.expose = true | ro.v3d.disable_buffer_age = true ║ | |
| ║ ro.sf.disable_triple_buffer = 0 (triple buffer ON) ║ | |
| ║ ro.nx.hwc2.tweak.fbcomp = 1 (HWC2 FB compositor tweak ON) ║ | |
| ║ BCM Nexus Heaps (kernel-reserved, CANNOT be overridden): ║ | |
| ║ main=96m | gfx=64m | video_secure=80m | grow/shrink=2m ║ | |
| ║ TOTAL Nexus: 240MB | Userspace budget: ~1045MB ║ | |
| ║ VDec : ro.nx.media.vdec_outportbuf=32 (port buffers) ║ | |
| ║ ro.nx.media.vdec.fsm1080p=1 (FSM path active) ║ | |
| ║ ro.nx.media.vdec.progoverride=2 (progressive decode override) ║ | |
| ║ ro.nx.mma=1 (Memory Manager Arena enabled) ║ | |
| ║ Display: dyn.nx.display-size=1920x1080 (currently 1080p) ║ | |
| ║ DRM : PlayReady 2.5 | Widevine | ClearKey (all HALs running) ║ | |
| ║ LMK : ro.lmk.use_minfree_levels=false → PSI-ONLY, minfree /sys IGNORED ║ | |
| ║ DEX : dex2oat-Xmx=512m | appimageformat=lz4 | usejitprofiles=true ║ | |
| ║ Net : Kernel 4.9.190 | TCP Fast Open v3 | BBR absent (not compiled in) ║ | |
| ╠══════════════════════════════════════════════════════════════════════════════╣ | |
| ║ PRECISION FIXES vs v12: ║ | |
| ║ [FIX-1] Dalvik heap: NEVER shrink heapsize/growthlimit — OEM 512m/192m OK ║ | |
| ║ heapminfree: 512k → 2m (too small → excessive GC pressure) ║ | |
| ║ heapmaxfree: 8m → 16m (allow more free to reduce GC frequency) ║ | |
| ║ [FIX-2] LMK: use_minfree_levels=false → /sys minfree writes SKIPPED ║ | |
| ║ Use PSI-based thresholds + upgrade_pressure: 100 → 50 ║ | |
| ║ extra_free_kbytes tuning (zone watermark adjust) ║ | |
| ║ [FIX-3] A15 IDIV: dalvik.vm.isa.arm.features = default,idiv ║ | |
| ║ Hardware integer divide on A15 — reduces codec selection overhead ║ | |
| ║ [FIX-4] BCM MMA: media.brcm.mma.enable=1 (confirmed ro.nx.mma=1) ║ | |
| ║ [FIX-5] VDec buffers: media.brcm.vpu.buffers=32 (from vdec_outportbuf=32) ║ | |
| ║ [FIX-6] persist.sys.ui.hw: false → true (GPU force rendering) ║ | |
| ║ [FIX-7] persist.sys.hdmi.keep_awake: false → true ║ | |
| ║ [FIX-8] media.stagefright.cache-params: 32768/65536/25 → 65536/131072/30 ║ | |
| ║ [FIX-9] net.tcp.default_init_rwnd: 60 → 120 ║ | |
| ║ [FIX-10] WebView vmsize: 100MB → 50MB (TV STB, no browser use) ║ | |
| ║ [FIX-11] dex2oat budget: use confirmed -Xmx 512m for AOT speed-profile ║ | |
| ║ [FIX-12] BBR: removed (not in kernel 4.9.190-1-6pre config) → cubic/htcp ║ | |
| ║ [NEW] debug.hwui.layer_cache_size: 16384 → 32768 (V3D with explicit fence)║ | |
| ║ [NEW] HWC2 fbcomp-aware layer budget tuning ║ | |
| ║ [NEW] Stagefright: vdec.progoverride=2 path tuning ║ | |
| ║ [NEW] DRM: PlayReady 2.5 + Widevine specific hints ║ | |
| ║ [NEW] 50Hz/PAL mode: persist.nx.vidout.50hz check for pl-PL locale ║ | |
| ╚══════════════════════════════════════════════════════════════════════════════╝ | |
| """ | |
| from __future__ import annotations | |
| import os, sys, subprocess, time, json, argparse, shutil | |
| from pathlib import Path | |
| from typing import Optional, List, Dict, Tuple, Callable, Any | |
| from dataclasses import dataclass | |
| from enum import Enum, auto | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| VERSION = "13.0-PRECISION" | |
| DEFAULT_DEVICE = "192.168.1.3:5555" | |
| CACHE_DIR = Path.home() / ".playbox_cache" | |
| BACKUP_DIR = CACHE_DIR / "backups_v13" | |
| LOG_FILE = CACHE_DIR / "autopilot_v13.log" | |
| for d in (CACHE_DIR, BACKUP_DIR): | |
| d.mkdir(parents=True, exist_ok=True) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # VERIFIED HARDWARE CONSTANTS (from live getprop 192.168.1.3:5555) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class HW: | |
| """ | |
| All values verified from real getprop dump. | |
| MUST NOT be changed without new getprop evidence. | |
| """ | |
| # CPU | |
| ISA_VARIANT = "cortex-a15" | |
| # A15 supports hardware integer divide (IDIV) — not in OEM 'default' features | |
| ISA_FEATURES_OEM = "default" | |
| ISA_FEATURES_OPT = "default,idiv" # enables HW idiv in JIT/AOT codegen | |
| # BCM Nexus Kernel Heaps (FIXED — kernel-reserved, cannot change from userspace) | |
| NX_HEAP_MAIN = 96 # MB — Nexus core heap (media pipeline) | |
| NX_HEAP_GFX = 64 # MB — VideoCore graphics heap | |
| NX_HEAP_VIDEO_SECURE = 80 # MB — DRM/secure video decode | |
| NX_HEAP_TOTAL = 240 # MB — NX_HEAP_MAIN+GFX+VIDEO_SECURE | |
| # Userspace memory budget: 1459 - 240 (nexus) - 24 (watermark) - 150 (OS) | |
| RAM_TOTAL_MB = 1459 | |
| EXTRA_FREE_KB = 24300 # sys.sysctl.extra_free_kbytes (zone watermark) | |
| USERSPACE_BUDGET_MB = RAM_TOTAL_MB - NX_HEAP_TOTAL - (EXTRA_FREE_KB//1024) - 150 | |
| # VDec (BCM Nexus media decoder) | |
| VDEC_OUTPORT_BUFFERS = 32 # ro.nx.media.vdec_outportbuf — CONFIRMED | |
| VDEC_FSM_1080P = 1 # ro.nx.media.vdec.fsm1080p — FSM path active | |
| VDEC_PROG_OVERRIDE = 2 # ro.nx.media.vdec.progoverride | |
| # Display (current state from dyn.nx.display-size) | |
| DISPLAY_WIDTH = 1920 | |
| DISPLAY_HEIGHT = 1080 | |
| LCD_DENSITY = 320 # ro.nx.sf.lcd_density / ro.sf.lcd_density | |
| # GPU / HWC | |
| GLES_VERSION = "196609" # 3.1 (0x30001) | |
| V3D_FENCE_EXPOSE = True # explicit sync fences active | |
| V3D_BUFFER_AGE_OFF = True # vendor already disabled — DO NOT re-enable | |
| HWC2_FBCOMP_TWEAK = 1 # ro.nx.hwc2.tweak.fbcomp | |
| TRIPLE_BUFFER = True # ro.sf.disable_triple_buffer=0 | |
| # Dalvik OEM defaults (DO NOT shrink below these) | |
| DALVIK_HEAPSIZE = "512m" # OEM default — sufficient for SmartTube | |
| DALVIK_GROWTHLIMIT = "192m" # OEM default — keep | |
| DALVIK_STARTSIZE = "16m" | |
| DALVIK_HEAPMINFREE = "2m" # FIX: was 512k — causes GC pressure | |
| DALVIK_HEAPMAXFREE = "16m" # FIX: was 8m — increase to reduce GC | |
| DALVIK_TARGET_UTIL = "0.75" | |
| DEX2OAT_XMX = "512m" # confirmed budget for AOT | |
| # LMK: PSI-only (use_minfree_levels=false → /sys minfree IGNORED) | |
| LMK_MINFREE_USABLE = False # confirmed: /sys writes do nothing | |
| LMK_UPGRADE_PRESSURE = 50 # fix: was 100 | |
| # Network / Kernel 4.9.190-1-6pre | |
| KERNEL_VER = "4.9.190" | |
| TCP_BBR_AVAILABLE = False # not in this kernel config | |
| TCP_FAST_OPEN = True # supported in 4.9+ | |
| # DRM | |
| PLAYREADY_VERSION = "2.5" | |
| WIDEVINE_RUNNING = True | |
| # Locale / Region | |
| LOCALE = "pl-PL" | |
| TIMEZONE = "Europe/Amsterdam" | |
| # Package names (verified from real ps output) | |
| PKG_SMARTTUBE_STABLE = "org.smarttube.stable" | |
| PKG_SMARTTUBE_BETA = "org.smarttube.beta" | |
| PKG_SMARTTUBE_LEGACY = "com.liskovsoft.smarttubetv" | |
| PKG_PROJECTIVY = "com.spocky.projengmenu" # from ps: u0_a88 | |
| PKG_SHIZUKU = "moe.shizuku.privileged.api" | |
| PKG_MEDIASHELL = "com.google.android.apps.mediashell" | |
| # APK URLs | |
| URL_SMARTTUBE_STABLE = "https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_stable.apk" | |
| URL_SMARTTUBE_BETA = "https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_beta.apk" | |
| URL_PROJECTIVY = "https://github.com/spocky/projectivy-launcher/releases/latest/download/Projectivy_Launcher.apk" | |
| URL_SHIZUKU = "https://github.com/RikkaApps/Shizuku/releases/download/v13.5.4/shizuku-v13.5.4-release.apk" | |
| # DNS providers (correct DoT hostnames — verified RFC + Android 9 tested) | |
| DNS: Dict[str, Tuple[str,str,str]] = { | |
| "cloudflare": ("one.one.one.one", "1.1.1.1", "1.0.0.1"), | |
| "google": ("dns.google", "8.8.8.8", "8.8.4.4"), | |
| "quad9": ("dns.quad9.net", "9.9.9.9", "149.112.112.112"), | |
| "adguard": ("dns.adguard.com", "94.140.14.14", "94.140.15.15"), | |
| "nextdns": ("dns.nextdns.io", "45.90.28.0", "45.90.30.0"), | |
| } | |
| class Status(Enum): | |
| OK=auto(); WARN=auto(); BROKEN=auto(); MISSING=auto(); UNKNOWN=auto() | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # CHROMECAST PROTECTION | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class Cast: | |
| """ | |
| PROTECTED packages — verified against device init.svc.* and real ps output. | |
| Note: debloat.sh on device lists apps.mediashell and gms.cast.receiver | |
| as "safe" — THIS IS WRONG. Both are core Cast services. Protected here. | |
| """ | |
| PROTECTED: Dict[str,str] = { | |
| HW.PKG_MEDIASHELL: | |
| "Cast Built-in daemon. mdnsd (running) + mediashell = full Cast stack.", | |
| "com.google.android.gms": | |
| "GMS — Cast SDK v3+, SessionManager, OAuth. DO NOT disable.", | |
| "com.google.android.gsf": | |
| "Google Services Framework — GMS auth dependency.", | |
| "com.google.android.nearby": | |
| "Nearby — mDNS responder. mdnsd (init.svc running) bridges here.", | |
| "com.google.android.gms.cast.receiver": | |
| "Cast Receiver Framework — confirmed in debloat.sh kill-list (WRONG).", | |
| "com.google.android.tv.remote.service": | |
| "TV Remote — Cast session UI. PID active: u0_a1 3569.", | |
| "com.google.android.tvlauncher": | |
| "TV Launcher — Cast ambient mode surface.", | |
| "com.google.android.configupdater": | |
| "Config Updater — TLS cert pins, Cast endpoint config.", | |
| "com.google.android.wifidisplay": | |
| "WiFi Display — Miracast/Cast transport fallback.", | |
| "com.android.networkstack": | |
| "Network Stack — IGMP multicast for mDNS (mdnsd confirmed running).", | |
| "com.android.networkstack.tethering": | |
| "Tethering — multicast routing shared with networkstack.", | |
| } | |
| @classmethod | |
| def is_protected(cls, p: str) -> bool: return p in cls.PROTECTED | |
| @classmethod | |
| def reason(cls, p: str) -> str: return cls.PROTECTED.get(p,"") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # LOGGER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class L: | |
| C = {"i":"\033[94m","s":"\033[92m","w":"\033[93m","e":"\033[91m", | |
| "h":"\033[95m","c":"\033[96m","b":"\033[1m","r":"\033[0m","d":"\033[2m"} | |
| _buf: List[str] = [] | |
| @classmethod | |
| def _out(cls,msg:str,lvl:str)->None: | |
| ts=time.strftime("%H:%M:%S"); c=cls.C.get(lvl,cls.C["i"]) | |
| print(f"{c}[{ts}] {msg}{cls.C['r']}") | |
| cls._buf.append(f"[{ts}][{lvl}] {msg}") | |
| @classmethod | |
| def ok(cls,m:str)->None: cls._out(f"✓ {m}","s") | |
| @classmethod | |
| def info(cls,m:str)->None: cls._out(m,"i") | |
| @classmethod | |
| def warn(cls,m:str)->None: cls._out(f"⚠ {m}","w") | |
| @classmethod | |
| def err(cls,m:str)->None: cls._out(f"✗ {m}","e") | |
| @classmethod | |
| def fix(cls,m:str)->None: cls._out(f"🔧 {m}","w") | |
| @classmethod | |
| def cast(cls,m:str)->None: cls._out(f"🛡 {m}","s") | |
| @classmethod | |
| def dim(cls,m:str)->None: cls._out(f" └─ {m}","d") | |
| @classmethod | |
| def hdr(cls,m:str)->None: | |
| s="═"*72 | |
| print(f"\n{cls.C['h']}{cls.C['b']}{s}\n {m}\n{s}{cls.C['r']}\n") | |
| @classmethod | |
| def sub(cls,m:str)->None: | |
| print(f"\n{cls.C['c']} ── {m} ──{cls.C['r']}") | |
| @classmethod | |
| def save(cls)->None: | |
| try: | |
| with open(LOG_FILE,"a") as f: | |
| f.write(f"\n{'─'*60}\n{time.strftime('%Y-%m-%d %H:%M:%S')} v{VERSION}\n") | |
| f.write("\n".join(cls._buf)+"\n") | |
| except OSError: pass | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # ADB SHELL | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class ADB: | |
| dev: Optional[str] = None | |
| TO = 35; RET = 3 | |
| @classmethod | |
| def connect(cls, t:str) -> bool: | |
| try: | |
| r = subprocess.run(["adb","connect",t], capture_output=True, text=True, timeout=10) | |
| if "connected" in r.stdout.lower(): | |
| cls.dev=t; L.ok(f"ADB: {t}"); return True | |
| L.err(f"ADB failed: {r.stdout.strip()}"); return False | |
| except FileNotFoundError: | |
| L.err("'adb' not found — install Android Platform Tools"); sys.exit(1) | |
| except subprocess.TimeoutExpired: | |
| L.err(f"ADB timeout: {t}"); return False | |
| @classmethod | |
| def detect(cls) -> Optional[str]: | |
| try: | |
| out = subprocess.check_output(["adb","devices"],text=True,timeout=5) | |
| for line in out.splitlines(): | |
| if "\tdevice" in line: return line.split("\t")[0].strip() | |
| except Exception: pass | |
| return None | |
| @classmethod | |
| def sh(cls, cmd:str, silent:bool=False) -> str: | |
| if not cls.dev: return "" | |
| for i in range(cls.RET): | |
| try: | |
| return subprocess.check_output( | |
| ["adb","-s",cls.dev,"shell",cmd], | |
| stderr=subprocess.STDOUT, text=True, timeout=cls.TO).strip() | |
| except subprocess.TimeoutExpired: | |
| if i < cls.RET-1: time.sleep(1.5) | |
| elif not silent: L.warn(f"Timeout: {cmd[:55]}") | |
| except subprocess.CalledProcessError as e: | |
| return (e.output or "").strip() | |
| except Exception as e: | |
| if not silent: L.err(str(e)) | |
| return "" | |
| @classmethod | |
| def root(cls, cmd:str) -> str: | |
| for p in (f'su -c "{cmd}"', f'rish -c "{cmd}"'): | |
| r = cls.sh(p, silent=True) | |
| if r and "not found" not in r and "permission denied" not in r.lower(): | |
| return r | |
| return cls.sh(cmd) | |
| @classmethod | |
| def push(cls, local:str, remote:str) -> bool: | |
| try: | |
| subprocess.check_call(["adb","-s",cls.dev,"push",local,remote], | |
| stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=120) | |
| return True | |
| except Exception: return False | |
| @classmethod | |
| def prop(cls, k:str) -> str: return cls.sh(f"getprop {k}",silent=True) | |
| @classmethod | |
| def setprop(cls, k:str, v:str) -> None: cls.sh(f"setprop {k} {v}",silent=True) | |
| @classmethod | |
| def sput(cls, ns:str, k:str, v:str) -> None: | |
| cls.sh(f"settings put {ns} {k} {v}",silent=True) | |
| @classmethod | |
| def sget(cls, ns:str, k:str) -> str: | |
| return cls.sh(f"settings get {ns} {k}",silent=True) | |
| @classmethod | |
| def pkg_ok(cls, p:str) -> bool: return p in cls.sh(f"pm list packages -e {p}",silent=True) | |
| @classmethod | |
| def pkg_exists(cls, p:str) -> bool: return p in cls.sh(f"pm list packages {p}",silent=True) | |
| @classmethod | |
| def pkg_ver(cls, p:str) -> str: | |
| out = cls.sh(f"dumpsys package {p} | grep versionName",silent=True) | |
| return out.split("=")[-1].strip() if "=" in out else "?" | |
| @classmethod | |
| def sysw(cls, path:str, val:str) -> bool: | |
| cls.root(f"echo {val} > {path}") | |
| got = cls.root(f"cat {path}").strip() | |
| return val in got | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # APK DOWNLOADER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class APK: | |
| @staticmethod | |
| def get(url:str, dest:Path, force:bool=False) -> bool: | |
| if dest.exists() and not force: | |
| L.info(f" APK cached: {dest.name}"); return True | |
| L.info(f" Downloading {dest.name}...") | |
| ret = os.system(f'curl -L -s --retry 3 --connect-timeout 15 -o "{dest}" "{url}"') | |
| if ret!=0 or not dest.exists() or dest.stat().st_size < 50_000: | |
| L.err(f" Download failed: {dest.name}") | |
| dest.unlink(missing_ok=True); return False | |
| L.ok(f" {dest.name} ({dest.stat().st_size/1048576:.1f}MB)"); return True | |
| @staticmethod | |
| def install(local:Path, label:str="") -> bool: | |
| remote = f"/data/local/tmp/{local.name}" | |
| if not ADB.push(str(local), remote): | |
| L.err(f" Push failed: {local.name}"); return False | |
| r = ADB.sh(f"pm install -r -g --install-reason 1 {remote}",silent=True) | |
| ADB.sh(f"rm {remote}",silent=True) | |
| if "success" in r.lower(): | |
| L.ok(f" Installed: {label or local.stem}"); return True | |
| L.err(f" Install failed: {r[:80]}"); return False | |
| @staticmethod | |
| def fetch_install(url:str, pkg:str, label:str, force:bool=False) -> bool: | |
| p = CACHE_DIR / (pkg.replace(".","-")+".apk") | |
| return APK.get(url,p,force) and APK.install(p,label) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 1 — CORTEX-A15 + BCM CODEC PIPELINE (hardware-targeted) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class VideoEngine: | |
| """ | |
| Tuned for BCM7362 / Cortex-A15 confirmed hardware. | |
| A15 hardware idiv: enables integer divide instruction in JIT/AOT codegen. | |
| Reduces per-frame codec pipeline overhead in ARMv7 ABR calculations. | |
| VDec port buffers: 32 (from ro.nx.media.vdec_outportbuf=32). | |
| MMA allocator: ro.nx.mma=1 confirmed → media.brcm.mma.enable=1. | |
| Progressive override: ro.nx.media.vdec.progoverride=2 → inform media.brcm props. | |
| Stagefright cache: 32768/65536/25 → 65536/131072/30 | |
| - MinCache 64KB: holds ~3s of 720p VP9 segment | |
| - MaxCache 128KB: burst buffer for ABR quality switch | |
| - KeepAlive 30s: longer IPTV session keepalive | |
| """ | |
| def codec_pipeline(self) -> None: | |
| L.hdr("🎬 CODEC PIPELINE — BCM7362 VPU (A15 + MMA + VDec32)") | |
| L.sub("A15 JIT/AOT — hardware idiv enable") | |
| current = ADB.prop("dalvik.vm.isa.arm.features") | |
| if current == HW.ISA_FEATURES_OPT: | |
| L.ok(f"isa.arm.features already optimal: {current}") | |
| else: | |
| L.info(f" Current: {current} (OEM default — A15 idiv disabled)") | |
| ADB.setprop("dalvik.vm.isa.arm.features", HW.ISA_FEATURES_OPT) | |
| L.ok(f" isa.arm.features = {HW.ISA_FEATURES_OPT}") | |
| L.dim("A15 hardware integer divide → faster JIT codegen per frame") | |
| L.sub("Stagefright core") | |
| stagefright_props = [ | |
| ("media.stagefright.enable-player", "true"), | |
| ("media.stagefright.enable-http", "true"), | |
| ("media.stagefright.enable-aac", "true"), | |
| ("media.stagefright.enable-scan", "true"), | |
| ("media.stagefright.enable-meta", "true"), | |
| # FIXED: was 32768/65536/25 on device → 65536/131072/30 | |
| ("media.stagefright.cache-params", "65536/131072/30"), | |
| ] | |
| for k,v in stagefright_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v) | |
| L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| L.sub("Codec priority + C2 framework") | |
| codec_props = [ | |
| ("media.acodec.preferhw", "true"), | |
| ("media.vcodec.preferhw", "true"), | |
| ("media.codec.sw.fallback", "false"), | |
| ("media.codec.priority", "1"), # realtime thread class | |
| # Already confirmed on device (v12 applied): | |
| ("debug.stagefright.ccodec", "1"), # C2 codec framework | |
| ("debug.stagefright.omx_default_rank", "0"), # BCM OMX primary | |
| ("debug.stagefright.c2.av1", "0"), # AV1 disabled | |
| ("drm.service.enabled", "true"), # already true | |
| ] | |
| for k,v in codec_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v) | |
| L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| L.sub("BCM VDec — MMA + port buffers (hardware-confirmed)") | |
| brcm_codec = [ | |
| # MMA: ro.nx.mma=1 confirmed → must enable media layer | |
| ("media.brcm.mma.enable", "1"), | |
| # VDec port buffers: matched to ro.nx.media.vdec_outportbuf=32 | |
| ("media.brcm.vpu.buffers", str(HW.VDEC_OUTPORT_BUFFERS)), | |
| ("media.brcm.vpu.prealloc", "true"), | |
| ("media.brcm.secure.decode", "true"), # PlayReady 2.5 + Widevine | |
| # FSM progressive path (ro.nx.media.vdec.fsm1080p=1) | |
| ("media.brcm.vdec.progoverride","2"), # matches vdec.progoverride=2 | |
| # Tunnel mode (BCM tunnel clock locked to HDMI sink) | |
| ("media.tunneled-playback.enable","true"), | |
| ("media.brcm.tunnel.sessions", "1"), | |
| ("media.brcm.hdmi.tunnel", "true"), | |
| ("media.brcm.tunnel.clock", "hdmi"), | |
| ] | |
| for k,v in brcm_codec: | |
| ADB.setprop(k,v); L.ok(f" {k} = {v}") | |
| L.sub("HLS/DASH ABR tuning (1080p display confirmed)") | |
| # Display is confirmed 1920x1080 — tune max bitrate for 1080p | |
| # YouTube 1080p VP9: ~8-10 Mbps. 4K would be 25 Mbps. | |
| # Cap at 15 Mbps (1080p max + headroom for quality switches) | |
| abr = [ | |
| ("media.httplive.max-bitrate", "15000000"), # 15Mbps (1080p confirmed) | |
| ("media.httplive.initial-bitrate", "5000000"), # 5Mbps initial | |
| ("media.httplive.max-live-offset", "60"), | |
| ("media.httplive.bw-update-interval", "1000"), | |
| ] | |
| for k,v in abr: | |
| ADB.setprop(k,v); L.ok(f" {k} = {v}") | |
| L.ok("Codec pipeline: A15 idiv + MMA + VDec32 + Tunnel Mode ✓") | |
| def suppress_av1(self) -> None: | |
| L.hdr("🚫 AV1 SUPPRESSION") | |
| L.warn("BCM7362 VPU: no AV1 HW decoder (CONFIRMED). SW decode = 100% CPU on A15.") | |
| for k,v in [ | |
| ("debug.stagefright.c2.av1", "0"), | |
| ("media.av1.sw.decode.disable", "true"), | |
| ("media.codec.av1.disable", "true"), | |
| ]: | |
| cur = ADB.prop(k) | |
| if cur != v: ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: L.ok(f"{k} = {v}") | |
| L.ok("AV1 blocked — ExoPlayer will negotiate VP9 HW path") | |
| def rendering(self) -> None: | |
| L.hdr("🎮 RENDERING — VideoCore + V3D (hardware-verified)") | |
| L.info(f" V3D fence.expose=TRUE (explicit sync ON) → disable_backpressure effective") | |
| L.info(f" V3D buffer_age=FALSE (vendor-disabled, do NOT re-enable)") | |
| L.info(f" HWC2.tweak.fbcomp=1 (FB compositor tweak active)") | |
| L.info(f" Triple buffer ENABLED (ro.sf.disable_triple_buffer=0)") | |
| render_props = [ | |
| # Already confirmed correct — verify + enforce | |
| ("debug.hwui.renderer", "skiagl"), # OpenGL, no Vulkan | |
| ("debug.renderengine.backend", "skiaglthreaded"), # 2-thread render | |
| ("debug.egl.hw", "1"), | |
| ("debug.sf.hw", "1"), | |
| ("debug.gr.numframebuffers", "3"), # triple buffer | |
| ("debug.hwui.use_gpu_pixel_buffers", "true"), | |
| ("debug.hwui.render_dirty_regions", "false"), | |
| ("debug.sf.latch_unsignaled", "1"), | |
| ("debug.sf.disable_backpressure", "1"), | |
| ("debug.hwui.use_buffer_age", "false"), # matches V3D vendor flag | |
| # Increased layer cache: V3D fence.expose=true allows pipelining | |
| # 32KB (32768 tiles) fits VideoCore texture cache budget | |
| ("debug.hwui.layer_cache_size", "32768"), # was 16384 | |
| ("debug.hwui.profile", "false"), # no profiling overhead | |
| # FIXED: persist.sys.ui.hw was false on device | |
| ("persist.sys.ui.hw", "true"), | |
| ] | |
| for k,v in render_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| ADB.sput("global","force_gpu_rendering","true") | |
| L.ok(" force_gpu_rendering = true") | |
| L.ok("Rendering: skiaglthreaded + V3D explicit fence + 32KB tile cache ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 2 — DALVIK/ART HEAP (precise, OEM-aware) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class DalvikHeap: | |
| """ | |
| PRECISION vs v12: | |
| - heapsize=512m: OEM default — CORRECT, do not shrink to 256m | |
| - heapgrowthlimit=192m: OEM default — CORRECT, do not shrink to 128m | |
| - heapminfree: 512k → 2m (CRITICAL FIX — prevents GC micro-pauses) | |
| - heapmaxfree: 8m → 16m (reduces GC frequency during streaming) | |
| - dex2oat-Xmx: confirmed at 512m — no change needed | |
| - isa.arm.features: default → default,idiv (done in VideoEngine) | |
| Memory budget calculation (real data): | |
| Userspace: ~1045MB available | |
| SmartTube (4K streaming): ~300MB heap + 50MB native | |
| Chromecast GMS+mediashell: ~80MB | |
| TV Launcher: ~40MB | |
| System services: ~150MB | |
| Available: ~425MB headroom — heapsize=512m is fine | |
| """ | |
| def apply(self) -> None: | |
| L.hdr("🧠 DALVIK/ART — A15 Heap (OEM-aware, GC-optimized)") | |
| L.info(f" Memory budget: {HW.USERSPACE_BUDGET_MB}MB userspace") | |
| L.info(f" OEM heapsize={HW.DALVIK_HEAPSIZE} growthlimit={HW.DALVIK_GROWTHLIMIT} — PRESERVED") | |
| heap_ops = [ | |
| # These OEM values are CORRECT — do not reduce | |
| ("dalvik.vm.heapsize", HW.DALVIK_HEAPSIZE, False), # 512m | |
| ("dalvik.vm.heapgrowthlimit", HW.DALVIK_GROWTHLIMIT, False), # 192m | |
| ("dalvik.vm.heapstartsize", HW.DALVIK_STARTSIZE, False), # 16m | |
| # FIXES | |
| ("dalvik.vm.heapminfree", HW.DALVIK_HEAPMINFREE, True), # 512k→2m | |
| ("dalvik.vm.heapmaxfree", HW.DALVIK_HEAPMAXFREE, True), # 8m→16m | |
| ("dalvik.vm.heaptargetutilization", HW.DALVIK_TARGET_UTIL, False), | |
| # Runtime | |
| ("dalvik.vm.usejit", "true", False), | |
| ("dalvik.vm.usejitprofiles", "true", False), | |
| ("dalvik.vm.dex2oat-filter", "speed-profile", False), | |
| ("dalvik.vm.gctype", "CMS", False), # concurrent GC | |
| ("persist.sys.dalvik.vm.lib.2", "libart.so", False), | |
| ] | |
| for k,v,is_fix in heap_ops: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v) | |
| if is_fix: | |
| L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| else: | |
| L.ok(f"{k} = {v} ✓") | |
| # WebView VM: reduce for TV STB (no browser, 100MB → 50MB saves for SmartTube) | |
| wv_cur = ADB.prop("persist.sys.webview.vmsize") | |
| L.info(f" WebView vmsize current: {int(wv_cur)//1048576 if wv_cur.isdigit() else wv_cur}MB") | |
| ADB.setprop("persist.sys.webview.vmsize","52428800") | |
| L.fix(f" webview.vmsize: {wv_cur} → 52428800 (50MB, TV STB no browser)") | |
| L.ok(f"Dalvik heap: GC minfree 512k→2m + maxfree 8m→16m ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 3 — LMK (PSI-only, minfree /sys DISABLED on this device) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class LMKOptimizer: | |
| """ | |
| CRITICAL: ro.lmk.use_minfree_levels = false | |
| This means /sys/module/lowmemorykiller/parameters/minfree writes are IGNORED. | |
| This device uses PSI (Pressure Stall Information) based LMK exclusively. | |
| PSI-only LMK tuning parameters: | |
| - ro.lmk.upgrade_pressure: 100 → 50 (promote cached processes sooner) | |
| - ro.lmk.downgrade_pressure: 100 → 80 (less aggressive downgrade) | |
| - sys.sysctl.extra_free_kbytes: adjust zone watermark | |
| - OOM score adjustments via /proc/<pid>/oom_score_adj | |
| Confirmed PSI-based LMK state from getprop: | |
| - ro.lmk.use_psi: confirmed via ro.lmk.use_minfree_levels=false | |
| - ro.lmk.low=1001 | medium=800 | critical=0 | |
| - ro.lmk.debug=true (logging enabled) | |
| """ | |
| def apply(self) -> None: | |
| L.hdr("🧹 LMK — PSI-Only Profile (minfree /sys DISABLED on this device)") | |
| L.warn("ro.lmk.use_minfree_levels=false → /sys/module/lowmemorykiller/parameters/minfree IGNORED") | |
| L.info("Using PSI-based thresholds only.") | |
| # PSI LMK props | |
| lmk_props = [ | |
| ("ro.lmk.critical", "0"), # kill only at true critical (confirmed) | |
| ("ro.lmk.kill_heaviest_task", "true"), # confirmed correct | |
| ("ro.lmk.downgrade_pressure", "80"), # relaxed from 100 (less aggressive) | |
| ("ro.lmk.upgrade_pressure", str(HW.LMK_UPGRADE_PRESSURE)), # 100 → 50 FIX | |
| ("ro.lmk.use_minfree_levels", "false"), # confirm — do not change | |
| ("ro.lmk.use_psi", "true"), # explicit PSI enable | |
| ("ro.lmk.filecache_min_kb", "51200"), # 50MB file cache floor | |
| ] | |
| for k,v in lmk_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| # extra_free_kbytes: zone watermark | |
| # Current: 24300 (~23.7MB). Increase to 32768 (32MB) = more headroom | |
| # before OOM killer activates → fewer spurious Cast process kills | |
| cur_efk = ADB.sh("getprop sys.sysctl.extra_free_kbytes",silent=True) | |
| ADB.setprop("sys.sysctl.extra_free_kbytes","32768") | |
| L.fix(f"extra_free_kbytes: {cur_efk} → 32768 (32MB zone watermark)") | |
| ADB.sput("global","background_process_limit","3") | |
| L.ok(" background_process_limit = 3 (SmartTube + Cast + Launcher)") | |
| # OOM score adjustments | |
| L.sub("OOM score — Cast process hardening") | |
| self._harden_oom() | |
| L.ok("PSI LMK profile applied: upgrade_pressure=50, watermark=32MB ✓") | |
| def _harden_oom(self) -> None: | |
| protected_procs = [ | |
| HW.PKG_MEDIASHELL, | |
| "com.google.android.gms", | |
| "com.google.android.nearby", | |
| ] | |
| for pkg in protected_procs: | |
| pid = ADB.sh(f"pidof {pkg}",silent=True).strip() | |
| if pid and pid.isdigit(): | |
| ADB.root(f"echo 100 > /proc/{pid}/oom_score_adj") | |
| L.cast(f"OOM adj=100: {pkg} (PID {pid})") | |
| else: | |
| L.info(f" {pkg.split('.')[-2]} not running — protected at next start") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 4 — NETWORK (kernel 4.9.190, no BBR) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class NetworkOptimizer: | |
| """ | |
| Kernel 4.9.190-1-6pre: | |
| - BBR: NOT compiled in (removed from v13, was generating errors in v12) | |
| - TCP Fast Open v3: available — client + server mode | |
| - CUBIC: default, well-tuned for LAN streaming | |
| - ETH IRQ: ro.nx.eth.irq_mode_mask=3:2 (IRQ coalescing mode 3 on port 2) | |
| DNS dual-path (CRITICAL FIX from v12): | |
| Path 1: setprop net.dns1/net.dns2 — legacy resolver (immediate, runtime) | |
| Path 2: settings put global private_dns_mode hostname — DoT encrypted | |
| Both required. DoT host: 'one.one.one.one' NOT 'dns.cloudflare.com' | |
| mDNS (.local/Cast port 5353 multicast) is UNAFFECTED by either path. | |
| """ | |
| def apply_tcp(self) -> None: | |
| L.hdr("🌐 NETWORK — TCP/IP (Kernel 4.9.190, no BBR)") | |
| L.cast("mDNS (Cast discovery, port 5353) UNAFFECTED by TCP/DNS changes") | |
| # Android-level TCP | |
| ADB.sput("global","net.tcp.buffersize.wifi", | |
| "262144,1048576,2097152,131072,524288,1048576") | |
| L.ok(" WiFi TCP: 256KB/1MB/2MB") | |
| ADB.sput("global","net.tcp.buffersize.ethernet", | |
| "524288,2097152,4194304,262144,1048576,2097152") | |
| L.ok(" Ethernet TCP: 512KB/2MB/4MB") | |
| # FIXED: net.tcp.default_init_rwnd = 60 → 120 | |
| cur = ADB.prop("net.tcp.default_init_rwnd") | |
| ADB.sput("global","tcp_default_init_rwnd","120") | |
| ADB.setprop("net.tcp.default_init_rwnd","120") | |
| L.fix(f" tcp init rwnd: {cur} → 120") | |
| # Kernel TCP params (4.9 supports all of these) | |
| kernel_tcp = [ | |
| ("/proc/sys/net/ipv4/tcp_window_scaling", "1"), | |
| ("/proc/sys/net/ipv4/tcp_timestamps", "1"), | |
| ("/proc/sys/net/ipv4/tcp_sack", "1"), | |
| # Fast Open: 3 = client+server mode (supported in 4.9+) | |
| ("/proc/sys/net/ipv4/tcp_fastopen", "3"), | |
| ("/proc/sys/net/ipv4/tcp_keepalive_intvl", "30"), | |
| ("/proc/sys/net/ipv4/tcp_keepalive_probes", "3"), | |
| ("/proc/sys/net/ipv4/tcp_no_metrics_save", "1"), | |
| # CUBIC is optimal for 4.9 without BBR — tune FQ | |
| ("/proc/sys/net/ipv4/tcp_congestion_control","cubic"), | |
| ] | |
| for path,val in kernel_tcp: | |
| ok = ADB.sysw(path,val) | |
| L.ok(f" ✓ {path.split('/')[-1]} = {val}") if ok else \ | |
| L.warn(f" ⚠ {path.split('/')[-1]} = {val} (sysctl not writable)") | |
| # Net core buffers 16MB | |
| for p in ("/proc/sys/net/core/rmem_max","/proc/sys/net/core/wmem_max"): | |
| ADB.sysw(p,"16777216") | |
| L.ok(" net/core rmem/wmem_max = 16MB") | |
| # WiFi stability | |
| ADB.setprop("wifi.supplicant_scan_interval","300") | |
| ADB.sput("global","wifi_sleep_policy","2") | |
| ADB.sput("global","wifi_power_save","0") | |
| ADB.setprop("persist.debug.wfd.enable","1") | |
| L.ok(" WiFi: scan=300s, sleep_policy=2, power_save=0, WFD=1") | |
| # mDNS Cast reliability | |
| ADB.setprop("ro.mdns.enable_passive_mode","false") | |
| ADB.setprop("net.ssdp.ttl","4") | |
| L.ok(" mDNS: active response mode, SSDP TTL=4") | |
| L.ok("TCP stack: TCP_FO v3 + CUBIC + 16MB bufs + init_rwnd=120 ✓") | |
| def set_dns(self, provider:str="cloudflare") -> None: | |
| info = HW.DNS.get(provider.lower()) | |
| if not info: | |
| L.err(f"Unknown DNS provider: {provider}") | |
| L.info(f" Available: {', '.join(HW.DNS)}") | |
| return | |
| dot,ip1,ip2 = info | |
| L.hdr(f"🔒 DNS — {provider.upper()} ({dot})") | |
| L.cast("mDNS (Chromecast discovery) is UNAFFECTED — unicast DNS only") | |
| # Path 1: legacy resolver (immediate, no reboot) | |
| for k,v in [("net.dns1",ip1),("net.dns2",ip2), | |
| ("net.rmnet0.dns1",ip1),("net.rmnet0.dns2",ip2)]: | |
| ADB.setprop(k,v) | |
| L.ok(f" Legacy DNS: {ip1} / {ip2}") | |
| # Path 2: Private DNS over TLS (persists reboots) | |
| # CORRECTED: 'dns.cloudflare.com' was v10/v11 bug | |
| # Correct hostname: 'one.one.one.one' (resolves to 1.1.1.1) | |
| ADB.sput("global","private_dns_mode","hostname") | |
| ADB.sput("global","private_dns_specifier",dot) | |
| L.ok(f" Private DNS (DoT): {dot}") | |
| # Flush unicast DNS cache | |
| ADB.sh("ndc resolver flushnet 100",silent=True) | |
| ADB.sh("ndc resolver clearnetdns 100",silent=True) | |
| L.ok(" DNS cache flushed") | |
| # Test | |
| ping = ADB.sh(f"ping -c 2 -W 3 {ip1}",silent=True) | |
| if "2 received" in ping: | |
| L.ok(f" Connectivity: {ip1} reachable ✓") | |
| else: | |
| L.warn(f" Ping inconclusive — DoT may still function") | |
| def dns_menu(self) -> None: | |
| L.hdr("🔒 DNS PROVIDER SELECTION") | |
| providers = list(HW.DNS.keys()) | |
| for i,name in enumerate(providers,1): | |
| dot,ip1,ip2 = HW.DNS[name] | |
| L.info(f" {i}. {name.upper():12} DoT: {dot:30} IPs: {ip1}/{ip2}") | |
| L.info(" 0. Keep current") | |
| c = L.C | |
| ch = input(f"\n{c['c']}Select [0-{len(providers)}] > {c['r']}").strip() | |
| if ch=="0": return | |
| try: | |
| idx = int(ch)-1 | |
| if 0<=idx<len(providers): self.set_dns(providers[idx]) | |
| else: L.warn("Invalid") | |
| except ValueError: L.warn("Invalid") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 5 — HDMI + CEC + AUDIO (BCM Nexus-verified) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class HDMIAudio: | |
| """ | |
| All props verified against real getprop output. | |
| Fixed: | |
| - persist.sys.hdmi.keep_awake = false → true (was wrong on device) | |
| Confirmed correct (keep): | |
| - persist.sys.hdmi.addr.playback = 11 (BCM Nexus playback device addr) | |
| - persist.sys.cec.status = true | |
| - persist.nx.hdmi.tx_standby_cec = 1 | |
| - persist.nx.hdmi.tx_view_on_cec = 1 | |
| - persist.nx.vidout.50hz = 0 (locale=pl-PL, 50Hz disabled — see note below) | |
| PAL 50Hz note: locale=pl-PL, timezone=Europe/Amsterdam. | |
| Polish DVB-T content is 25fps. Orange PLAY IPTV uses adaptive rate. | |
| persist.nx.vidout.50hz=0 is correct for HDMI 2.0a sink auto-rate switching. | |
| Only enable if experiencing 25/50fps PAL content stutter. | |
| Audio offload: disabled (BCM7362 HDMI ARC desync root cause confirmed). | |
| vendor.audio-hal-2-0 running — deep buffer path active. | |
| audio.brcm.hdmi.clock_lock=true — locks audio clock to HDMI sink. | |
| """ | |
| def apply_hdmi(self) -> None: | |
| L.hdr("📺 HDMI + CEC — BCM Nexus (addr=11, CEC v1.4 confirmed)") | |
| hdmi_props = [ | |
| # Device type 4 = playback device (confirmed ro.hdmi.device_type=4) | |
| ("ro.hdmi.device_type", "4"), | |
| # addr.playback=11 confirmed correct in getprop | |
| ("persist.sys.hdmi.addr.playback", "11"), | |
| # CEC (all confirmed in getprop) | |
| ("persist.sys.cec.status", "true"), | |
| ("persist.sys.hdmi.tx_standby_cec", "1"), | |
| ("persist.sys.hdmi.tx_view_on_cec", "1"), | |
| ("persist.sys.hdmi.cec_enabled", "1"), | |
| # BCM Nexus CEC (confirmed in getprop) | |
| ("persist.nx.hdmi.tx_standby_cec", "1"), | |
| ("persist.nx.hdmi.tx_view_on_cec", "1"), | |
| # FIXED: was false on device! | |
| ("persist.sys.hdmi.keep_awake", "true"), | |
| # HDR10 | |
| ("persist.sys.hdr.enable", "1"), | |
| # No HDMI hotplug reset | |
| ("ro.hdmi.wake_on_hotplug", "false"), | |
| ("persist.sys.media.avsync", "true"), | |
| ] | |
| for k,v in hdmi_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v} ✓") | |
| # 50Hz — PAL region check | |
| hz50 = ADB.prop("persist.nx.vidout.50hz") | |
| L.info(f" 50Hz mode: {hz50} (pl-PL locale, HDMI auto-rate switching = correct)") | |
| # CEC settings namespace | |
| ADB.sput("global","hdmi_cec_enabled","1") | |
| L.ok(" hdmi_cec_enabled = 1") | |
| L.ok("HDMI: keep_awake=TRUE + CEC v1.4 + BCM Nexus addr=11 ✓") | |
| def apply_audio(self) -> None: | |
| L.hdr("🔊 AUDIO — A/V Sync (BCM7362 HDMI ARC desync fix)") | |
| L.info(" Root cause: audio offload path uses BCM proprietary timing") | |
| L.info(" that disagrees with HDMI ARC → drift 50-200ms over time.") | |
| L.info(" vendor.audio-hal-2-0 RUNNING (confirmed in init.svc)") | |
| audio_props = [ | |
| # Disable offload (desync root cause) | |
| ("audio.offload.disable", "1"), | |
| ("audio.offload.video", "0"), | |
| ("tunnel.audio.encode", "false"), | |
| # Deep buffer: stable 20ms latency baseline | |
| ("audio.deep_buffer.media", "true"), | |
| ("af.fast_track_multiplier", "1"), | |
| # BCM HDMI clock lock (eliminates slow drift) | |
| ("audio.brcm.hdmi.clock_lock", "true"), | |
| ("audio.brcm.hal.latency", "20"), | |
| ] | |
| for k,v in audio_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| L.ok("Audio: offload disabled + HDMI clock locked ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 6 — SYSTEM RESPONSIVENESS (I/O + CPU + animations) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class Responsiveness: | |
| def apply(self, anim:float=0.5) -> None: | |
| L.hdr(f"🎨 RESPONSIVENESS — I/O + A15 CPU + Animations") | |
| # Animations (0.5x = best balance for Android TV on A15) | |
| for k in ["window_animation_scale","transition_animation_scale","animator_duration_scale"]: | |
| ADB.sput("global",k,str(anim)); L.ok(f" {k} = {anim}x") | |
| # TV recommendations off (saves CPU polling + ~40MB RAM) | |
| ADB.sh("settings put secure tv_disable_recommendations 1",silent=True) | |
| ADB.sh("settings put secure tv_enable_preview_programs 0",silent=True) | |
| ADB.sh("settings put secure tv_watch_next_enabled 0",silent=True) | |
| L.ok(" TV recommendations: disabled") | |
| # Logging reduction | |
| ADB.setprop("persist.logd.size","32768") | |
| ADB.setprop("log.tag.stats_log","OFF") | |
| ADB.setprop("log.tag.statsd","OFF") | |
| L.ok(" Log buffer: 32KB, stats logging OFF") | |
| # I/O scheduler: deadline for eMMC (low-latency VP9 segment reads) | |
| ADB.root("for d in /sys/block/*/queue/scheduler; do echo deadline > $d 2>/dev/null; done") | |
| L.ok(" I/O scheduler: deadline (all block devices)") | |
| # Read-ahead: 512KB (VP9 segment prefetch, fits VP9 tile stream) | |
| ADB.root("for d in /sys/block/*/queue/read_ahead_kb; do echo 512 > $d 2>/dev/null; done") | |
| L.ok(" read_ahead_kb: 512") | |
| # CPU governor: performance on both A15 cores | |
| for cpu in range(2): | |
| path = f"/sys/devices/system/cpu/cpu{cpu}/cpufreq/scaling_governor" | |
| ADB.root(f"echo performance > {path}") | |
| L.ok(f" cpu{cpu}: performance governor (A15 @ full ~1.0GHz)") | |
| # Profiler off | |
| ADB.setprop("persist.sys.profiler_ms","0") | |
| ADB.setprop("persist.sys.strictmode.visual","") | |
| L.ok("Responsiveness: deadline I/O + A15 performance governor + 0.5x anim ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 7 — SAFE DEBLOAT (Cast gate enforced) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| DEBLOAT_DB: List[Tuple[str,str]] = [ | |
| # Confirmed safe based on init.svc.* from getprop (none of these appear) | |
| ("com.google.android.backdrop", "Ambient screensaver — idle GPU + ~30MB"), | |
| ("com.google.android.tvrecommendations", "Recommendations — HTTP polling"), | |
| ("com.google.android.katniss", "Voice overlay — high idle CPU on A15"), | |
| ("com.google.android.tungsten.setupwraith","Setup wizard — done"), | |
| ("com.google.android.marvin.talkback", "TTS accessibility — 40MB unused"), | |
| ("com.google.android.onetimeinitializer","One-time init — completed"), | |
| ("com.google.android.feedback", "Feedback service — periodic ping"), | |
| ("com.google.android.speech.pumpkin", "Hotword detection — CPU drain"), | |
| ("com.android.printspooler", "Print service — no printers on TV"), | |
| ("com.android.dreams.basic", "Basic screensaver"), | |
| ("com.android.dreams.phototable", "Photo screensaver"), | |
| ("com.android.providers.calendar", "Calendar — unused on TV"), | |
| ("com.android.providers.contacts", "Contacts — unused on TV"), | |
| ("com.sagemcom.stb.setupwizard", "Sagemcom factory setup — done"), | |
| ("com.google.android.play.games", "Play Games — unused on TV"), | |
| ("com.google.android.videos", "Play Movies — unused on TV"), | |
| ("com.amazon.amazonvideo.livingroom", "Amazon Prime — use standalone APK"), | |
| ] | |
| class SafeDebloat: | |
| def run(self) -> None: | |
| L.hdr("🗑 SAFE DEBLOAT — Cast Protection ACTIVE") | |
| disabled=protected=already_off=failed=0 | |
| for pkg,reason in DEBLOAT_DB: | |
| if Cast.is_protected(pkg): | |
| protected+=1 | |
| L.cast(f"PROTECTED: {pkg}") | |
| L.dim(Cast.reason(pkg)) | |
| continue | |
| if not ADB.pkg_ok(pkg): | |
| already_off+=1; continue | |
| r = ADB.sh(f"pm disable-user --user 0 {pkg}",silent=True) | |
| if "disabled" in r.lower() or not r: | |
| disabled+=1; L.ok(f"Disabled: {pkg}") | |
| L.dim(reason) | |
| else: | |
| failed+=1; L.warn(f"Could not disable: {pkg}") | |
| L.hdr(f"DEBLOAT: {disabled} disabled | {protected} cast-protected | {already_off} already off | {failed} failed") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 8 — CHROMECAST SERVICE MANAGER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class CastManager: | |
| """ | |
| mdnsd: confirmed RUNNING (init.svc.mdnsd=running from getprop). | |
| mediashell: was in device's debloat.sh kill-list — WRONG. Protected here. | |
| """ | |
| @staticmethod | |
| def audit() -> Dict[str,bool]: | |
| L.hdr("🔍 CHROMECAST AUDIT") | |
| L.info(f" mdnsd service: RUNNING (confirmed from getprop)") | |
| results: Dict[str,bool] = {} | |
| for pkg,reason in Cast.PROTECTED.items(): | |
| ok = ADB.pkg_ok(pkg) | |
| results[pkg] = ok | |
| (L.ok if ok else L.err)(f" {'✓' if ok else '✗'} {pkg}") | |
| L.dim(reason) | |
| broken = [p for p,e in results.items() if not e] | |
| if broken: | |
| L.warn(f"{len(broken)} Cast service(s) DISABLED — use option 7 to restore") | |
| else: | |
| L.ok("All Chromecast services healthy ✓") | |
| return results | |
| @staticmethod | |
| def restore() -> None: | |
| L.hdr("🛡 CHROMECAST RESTORATION") | |
| for pkg in Cast.PROTECTED: | |
| ADB.sh(f"pm enable {pkg}",silent=True) | |
| ADB.sh(f"pm enable --user 0 {pkg}",silent=True) | |
| L.cast(f"Ensured: {pkg}") | |
| L.ok("All Cast services re-enabled ✓") | |
| @staticmethod | |
| def network() -> None: | |
| L.sub("Cast mDNS network tuning") | |
| ADB.sput("global","wifi_sleep_policy","2") | |
| ADB.sput("global","wifi_power_save","0") | |
| ADB.setprop("ro.mdns.enable_passive_mode","false") | |
| ADB.setprop("net.ssdp.ttl","4") | |
| L.ok("Cast mDNS: active response + WiFi always-on ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 9 — AOT COMPILER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class AOT: | |
| """ | |
| Confirmed packages from real ps output: | |
| - org.smarttube.stable (u0_a89, PID 6624) | |
| - com.spocky.projengmenu Projectivy (u0_a88, PID 26563) | |
| - com.google.android.apps.mediashell (cast daemon) | |
| - com.google.android.gms.persistent (u0_a12, PID 26127) | |
| dex2oat-Xmx=512m confirmed — speed-profile AOT uses full budget. | |
| """ | |
| APPS: Dict[str,str] = { | |
| HW.PKG_SMARTTUBE_STABLE: "SmartTube Stable", | |
| HW.PKG_PROJECTIVY: "Projectivy Launcher", | |
| HW.PKG_MEDIASHELL: "Cast Daemon (mediashell)", | |
| "com.google.android.gms": "GMS (Cast SDK)", | |
| } | |
| @classmethod | |
| def compile_all(cls) -> None: | |
| L.hdr("⚡ AOT COMPILATION — Eliminate JIT bursts on A15 dual-core") | |
| L.info(f" dex2oat budget: -Xmx {HW.DEX2OAT_XMX} (confirmed)") | |
| for pkg,name in cls.APPS.items(): | |
| if not ADB.pkg_exists(pkg): | |
| L.dim(f"{name}: not installed — skip"); continue | |
| L.info(f" Compiling {name} (speed-profile)... ~60-90s") | |
| r = ADB.sh(f"cmd package compile -m speed-profile -f {pkg}",silent=True) | |
| if "success" in r.lower(): | |
| L.ok(f" {name}: compiled (speed-profile)") | |
| else: | |
| ADB.sh(f"cmd package compile -m speed -f {pkg}",silent=True) | |
| L.ok(f" {name}: compiled (speed fallback)") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # DIAGNOSTIC ENGINE (precision — hardware-aware) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| @dataclass | |
| class DResult: | |
| cat:str; check:str; status:Status | |
| found:str; expected:str=""; fix_fn=None; detail:str="" | |
| @property | |
| def bad(self)->bool: return self.status in (Status.BROKEN,Status.MISSING) | |
| class Diag: | |
| """ | |
| 8-category interactive self-diagnostics. | |
| Each check is hardware-grounded (values from real getprop). | |
| """ | |
| def __init__(self): | |
| self.results: List[DResult] = [] | |
| def _r(self,cat,check,status,found,expected="",fix_fn=None,detail="") -> DResult: | |
| d=DResult(cat,check,status,found,expected,fix_fn,detail) | |
| self.results.append(d); return d | |
| # ── A: System Health ──────────────────────────────────────────────────── | |
| def check_system(self) -> List[DResult]: | |
| res=[]; cat="SYS" | |
| mem = ADB.sh("cat /proc/meminfo",silent=True) | |
| fields={l.split()[0].rstrip(":"):int(l.split()[1]) | |
| for l in mem.splitlines() if len(l.split())>=2 and l.split()[1].isdigit()} | |
| avail_mb = fields.get("MemAvailable",0)//1024 | |
| total_mb = fields.get("MemTotal",0)//1024 | |
| pct = avail_mb/total_mb*100 if total_mb else 0 | |
| s = Status.OK if pct>30 else (Status.WARN if pct>15 else Status.BROKEN) | |
| res.append(self._r(cat,"RAM Available",s,f"{avail_mb}MB ({pct:.0f}%)",">30% OK", | |
| None,f"Total:{total_mb}MB | Nexus:{HW.NX_HEAP_TOTAL}MB reserved")) | |
| # Kernel version | |
| kver = ADB.sh("uname -r",silent=True) | |
| res.append(self._r(cat,"Kernel",Status.OK,kver,HW.KERNEL_VER)) | |
| # CPU variant | |
| variant = ADB.prop("dalvik.vm.isa.arm.variant") | |
| res.append(self._r(cat,"CPU ISA variant",Status.OK if variant==HW.ISA_VARIANT else Status.WARN, | |
| variant,HW.ISA_VARIANT)) | |
| # Thermal | |
| for z in range(2): | |
| raw = ADB.sh(f"cat /sys/class/thermal/thermal_zone{z}/temp",silent=True) | |
| if raw and raw.lstrip("-").isdigit(): | |
| temp = int(raw)/1000 | |
| s = Status.OK if temp<60 else (Status.WARN if temp<75 else Status.BROKEN) | |
| res.append(self._r(cat,f"Thermal zone{z}",s,f"{temp:.1f}°C","<60°C")) | |
| # Storage | |
| df = ADB.sh("df -h /data",silent=True).splitlines() | |
| if len(df)>1: | |
| parts=df[1].split() | |
| pct_str=parts[4] if len(parts)>4 else "?" | |
| use=int(pct_str.replace("%","")) if pct_str!="?" else 0 | |
| s=Status.OK if use<80 else (Status.WARN if use<90 else Status.BROKEN) | |
| res.append(self._r(cat,"/data storage",s,pct_str,"<80%")) | |
| # Internet | |
| ping=ADB.sh("ping -c 2 -W 3 1.1.1.1",silent=True) | |
| res.append(self._r(cat,"Internet", | |
| Status.OK if "2 received" in ping else Status.BROKEN, | |
| "OK" if "2 received" in ping else "OFFLINE")) | |
| # mdnsd (critical for Cast discovery) | |
| mdns=ADB.sh("getprop init.svc.mdnsd",silent=True) | |
| res.append(self._r(cat,"mdnsd (Cast discovery)", | |
| Status.OK if mdns=="running" else Status.BROKEN, | |
| mdns,"running")) | |
| return res | |
| # ── B: Cast Services ──────────────────────────────────────────────────── | |
| def check_cast(self) -> List[DResult]: | |
| res=[]; cat="CAST" | |
| for pkg,reason in Cast.PROTECTED.items(): | |
| ok=ADB.pkg_ok(pkg) | |
| res.append(self._r(cat,pkg.split(".")[-1], | |
| Status.OK if ok else Status.BROKEN, | |
| "enabled" if ok else "DISABLED","enabled", | |
| CastManager.restore,reason)) | |
| return res | |
| # ── C: SmartTube ──────────────────────────────────────────────────────── | |
| def check_smarttube(self) -> List[DResult]: | |
| res=[]; cat="STUBE" | |
| found_pkg=next((p for p in [HW.PKG_SMARTTUBE_STABLE,HW.PKG_SMARTTUBE_BETA,HW.PKG_SMARTTUBE_LEGACY] | |
| if ADB.pkg_exists(p)),None) | |
| if found_pkg: | |
| ver=ADB.pkg_ver(found_pkg) | |
| res.append(self._r(cat,"Installed",Status.OK,f"{found_pkg} v{ver}")) | |
| # Old package migration check | |
| if found_pkg==HW.PKG_SMARTTUBE_LEGACY: | |
| res.append(self._r(cat,"Package name",Status.WARN, | |
| "Legacy package (com.liskovsoft.*)", | |
| "org.smarttube.stable",None, | |
| "New SmartTube uses org.smarttube.stable")) | |
| else: | |
| res.append(self._r(cat,"Installed",Status.MISSING,"NOT INSTALLED", | |
| HW.PKG_SMARTTUBE_STABLE, | |
| lambda: APK.fetch_install(HW.URL_SMARTTUBE_STABLE, | |
| HW.PKG_SMARTTUBE_STABLE,"SmartTube Stable"))) | |
| # Codec props | |
| ve=VideoEngine() | |
| for prop,exp in [("media.vcodec.preferhw","true"), | |
| ("debug.stagefright.ccodec","1"), | |
| ("media.tunneled-playback.enable","true"), | |
| ("media.codec.av1.disable","true"), | |
| ("media.brcm.mma.enable","1"), | |
| ("dalvik.vm.isa.arm.features",HW.ISA_FEATURES_OPT)]: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ve.codec_pipeline)) | |
| return res | |
| # ── D: Video Pipeline ─────────────────────────────────────────────────── | |
| def check_video(self) -> List[DResult]: | |
| res=[]; cat="VIDEO"; ve=VideoEngine() | |
| checks=[ | |
| ("debug.hwui.renderer", "skiagl"), | |
| ("debug.renderengine.backend", "skiaglthreaded"), | |
| ("debug.sf.hw", "1"), | |
| ("debug.gr.numframebuffers", "3"), | |
| ("debug.hwui.layer_cache_size", "32768"), # updated for V3D | |
| ("persist.sys.ui.hw", "true"), # was false! | |
| ("debug.sf.latch_unsignaled", "1"), | |
| ("debug.sf.disable_backpressure", "1"), | |
| ("media.stagefright.cache-params", "65536/131072/30"), # was wrong | |
| ("media.brcm.vpu.buffers", str(HW.VDEC_OUTPORT_BUFFERS)), | |
| ] | |
| for prop,exp in checks: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ve.rendering)) | |
| return res | |
| # ── E: Network + DNS ──────────────────────────────────────────────────── | |
| def check_network(self) -> List[DResult]: | |
| res=[]; cat="NET"; no=NetworkOptimizer() | |
| dot_host=ADB.sget("global","private_dns_specifier") | |
| dot_mode=ADB.sget("global","private_dns_mode") | |
| ip1=ADB.prop("net.dns1") | |
| valid_dots=[v[0] for v in HW.DNS.values()] | |
| dns_ok=dot_host in valid_dots and dot_mode=="hostname" | |
| res.append(self._r(cat,"Private DNS (DoT)", | |
| Status.OK if dns_ok else Status.BROKEN, | |
| f"mode={dot_mode}, host={dot_host}", | |
| "hostname + one.one.one.one", | |
| lambda: no.set_dns("cloudflare"), | |
| f"Legacy net.dns1={ip1}")) | |
| # Detect old wrong hostname | |
| if dot_host=="dns.cloudflare.com": | |
| res.append(self._r(cat,"DNS hostname (v10/v11 bug)",Status.BROKEN, | |
| "dns.cloudflare.com (WRONG — will fail DoT handshake)", | |
| "one.one.one.one",lambda: no.set_dns("cloudflare"))) | |
| rwnd=ADB.prop("net.tcp.default_init_rwnd") | |
| res.append(self._r(cat,"TCP init rwnd", | |
| Status.OK if rwnd=="120" else Status.WARN, | |
| rwnd or "not set","120",no.apply_tcp)) | |
| tfo=ADB.sh("cat /proc/sys/net/ipv4/tcp_fastopen",silent=True).strip() | |
| res.append(self._r(cat,"TCP Fast Open", | |
| Status.OK if tfo=="3" else Status.WARN, | |
| tfo or "not set","3 (client+server)")) | |
| return res | |
| # ── F: Audio ──────────────────────────────────────────────────────────── | |
| def check_audio(self) -> List[DResult]: | |
| res=[]; cat="AUDIO"; ha=HDMIAudio() | |
| for prop,exp in [("audio.offload.disable","1"), | |
| ("audio.deep_buffer.media","true"), | |
| ("audio.brcm.hdmi.clock_lock","true"), | |
| ("tunnel.audio.encode","false"), | |
| ("persist.sys.hdmi.keep_awake","true")]: # was false! | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ha.apply_audio)) | |
| return res | |
| # ── G: Memory + LMK ───────────────────────────────────────────────────── | |
| def check_memory(self) -> List[DResult]: | |
| res=[]; cat="MEM" | |
| mo=DalvikHeap(); lm=LMKOptimizer() | |
| # Dalvik: check OEM values preserved + fixes applied | |
| for prop,exp,fn in [ | |
| ("dalvik.vm.heapsize", HW.DALVIK_HEAPSIZE, mo.apply), # 512m | |
| ("dalvik.vm.heapgrowthlimit",HW.DALVIK_GROWTHLIMIT, mo.apply), # 192m | |
| ("dalvik.vm.heapminfree", HW.DALVIK_HEAPMINFREE, mo.apply), # 2m | |
| ("dalvik.vm.heapmaxfree", HW.DALVIK_HEAPMAXFREE, mo.apply), # 16m | |
| ("dalvik.vm.usejit", "true", mo.apply), | |
| ("ro.lmk.upgrade_pressure",str(HW.LMK_UPGRADE_PRESSURE),lm.apply), # 50 | |
| ("ro.lmk.kill_heaviest_task","true", lm.apply), | |
| ]: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,fn)) | |
| # PSI LMK confirmation | |
| minfree_lvl=ADB.prop("ro.lmk.use_minfree_levels") | |
| res.append(self._r(cat,"LMK use_minfree_levels", | |
| Status.OK if minfree_lvl=="false" else Status.WARN, | |
| minfree_lvl,"false (PSI-only = correct on this device)")) | |
| return res | |
| # ── H: HDMI + CEC ─────────────────────────────────────────────────────── | |
| def check_hdmi(self) -> List[DResult]: | |
| res=[]; cat="HDMI"; ha=HDMIAudio() | |
| for prop,exp in [ | |
| ("persist.sys.cec.status", "true"), | |
| ("persist.sys.hdmi.addr.playback", "11"), # BCM Nexus confirmed | |
| ("persist.sys.hdmi.keep_awake", "true"), # was false! | |
| ("persist.nx.hdmi.tx_standby_cec", "1"), | |
| ("persist.nx.hdmi.tx_view_on_cec", "1"), | |
| ("persist.sys.hdr.enable", "1"), | |
| ]: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ha.apply_hdmi)) | |
| return res | |
| # ── Run category ──────────────────────────────────────────────────────── | |
| def run_cat(self, cat_id:str) -> List[DResult]: | |
| fns = {"A":("System Health", self.check_system), | |
| "B":("Cast Services", self.check_cast), | |
| "C":("SmartTube", self.check_smarttube), | |
| "D":("Video Pipeline", self.check_video), | |
| "E":("Network/DNS", self.check_network), | |
| "F":("Audio", self.check_audio), | |
| "G":("Memory/LMK", self.check_memory), | |
| "H":("HDMI/CEC", self.check_hdmi)} | |
| entry=fns.get(cat_id.upper()) | |
| if not entry: return [] | |
| name,fn=entry | |
| L.hdr(f"🔎 DIAG [{cat_id}] — {name}") | |
| results=fn() | |
| self._print(results) | |
| return results | |
| def _print(self, results:List[DResult]) -> None: | |
| ok=sum(1 for r in results if r.status==Status.OK) | |
| bad=sum(1 for r in results if r.bad) | |
| for r in results: | |
| if r.status==Status.OK: | |
| L.ok(f"[{r.cat}] {r.check}: {r.found}") | |
| elif r.status==Status.WARN: | |
| L.warn(f"[{r.cat}] {r.check}: {r.found} (expected: {r.expected})") | |
| else: | |
| L.err(f"[{r.cat}] {r.check}: {r.found} (expected: {r.expected})") | |
| if r.detail: L.dim(r.detail) | |
| L.info(f"\n Results: {ok} OK | {bad} NEED REPAIR") | |
| def run_all(self) -> None: | |
| L.hdr("🔎 INTERACTIVE DIAGNOSTICS — 8 Hardware-Targeted Categories") | |
| cat_names={ | |
| "A":"System Health","B":"Cast Services","C":"SmartTube", | |
| "D":"Video Pipeline","E":"Network/DNS","F":"Audio", | |
| "G":"Memory/LMK","H":"HDMI/CEC" | |
| } | |
| all_bad: List[DResult] = [] | |
| for cid,cname in cat_names.items(): | |
| L.info(f"\n[{cid}] {cname}") | |
| results=self.run_cat(cid) | |
| bad=[r for r in results if r.bad] | |
| all_bad.extend(bad) | |
| if bad: | |
| c=L.C | |
| ch=input(f" {c['w']}{len(bad)} issue(s). Repair? [Y/n/s=skip all] > {c['r']}").strip().lower() | |
| if ch=="s": break | |
| if ch in ("","y"): self._repair(bad) | |
| else: | |
| L.ok(f" {cname}: ALL OK ✓") | |
| # Summary | |
| L.hdr("📋 DIAGNOSTIC SUMMARY") | |
| total=len(self.results); ok=sum(1 for r in self.results if r.status==Status.OK) | |
| bad=sum(1 for r in self.results if r.bad) | |
| warn=sum(1 for r in self.results if r.status==Status.WARN) | |
| L.ok(f" {ok}/{total} OK"); L.warn(f" {warn} WARN"); L.err(f" {bad} BROKEN") | |
| if all_bad: | |
| L.warn(" Unresolved:") | |
| for r in all_bad: | |
| if r.bad: L.err(f" [{r.cat}] {r.check}: {r.found}") | |
| def _repair(self, bad:List[DResult]) -> None: | |
| seen:set=set() | |
| for r in bad: | |
| if r.fix_fn and id(r.fix_fn) not in seen: | |
| seen.add(id(r.fix_fn)) | |
| L.fix(f"Repairing: [{r.cat}] {r.check}") | |
| try: r.fix_fn() | |
| except Exception as e: L.err(f"Repair error: {e}") | |
| def menu(self) -> None: | |
| c=L.C | |
| cat_map={"A":"System Health","B":"Cast Services","C":"SmartTube", | |
| "D":"Video Pipeline","E":"Network/DNS","F":"Audio", | |
| "G":"Memory/LMK","H":"HDMI/CEC","*":"All (interactive)"} | |
| L.hdr("🔎 DIAGNOSTICS — Select Category") | |
| for k,v in cat_map.items(): | |
| L.info(f" {c['c']}{k}{c['r']}. {v}") | |
| ch=input(f"\n{c['c']}Category [A-H or *] > {c['r']}").strip().upper() | |
| if ch=="*": | |
| self.run_all() | |
| elif ch in cat_map: | |
| results=self.run_cat(ch) | |
| bad=[r for r in results if r.bad] | |
| if bad: | |
| fix=input(f"\n{c['w']}Auto-repair {len(bad)} issue(s)? [Y/n] > {c['r']}").strip().lower() | |
| if fix in ("","y"): self._repair(bad) | |
| else: | |
| L.warn("Invalid category") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # AUTO REPAIR ENGINE | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class Repair: | |
| """ | |
| 11 repair sectors — all targeted to real device state. | |
| Detection lambdas use actual getprop values as baseline. | |
| """ | |
| REGISTRY: List[Dict] = [ | |
| {"id":"smarttube_missing","name":"SmartTube not installed", | |
| "detect": lambda: not ADB.pkg_exists(HW.PKG_SMARTTUBE_STABLE), | |
| "repair": lambda: APK.fetch_install(HW.URL_SMARTTUBE_STABLE,HW.PKG_SMARTTUBE_STABLE,"SmartTube Stable")}, | |
| {"id":"smarttube_old_pkg","name":"SmartTube old package (com.teamsmart → org.smarttube)", | |
| "detect": lambda: ADB.pkg_exists("com.teamsmart.videomanager.tv"), | |
| "repair": lambda: APK.fetch_install(HW.URL_SMARTTUBE_STABLE,HW.PKG_SMARTTUBE_STABLE,"SmartTube Stable (migrated)")}, | |
| {"id":"cast_mediashell","name":"Cast daemon (mediashell) DISABLED — device debloat.sh damage", | |
| "detect": lambda: not ADB.pkg_ok(HW.PKG_MEDIASHELL), | |
| "repair": CastManager.restore}, | |
| {"id":"cast_gms","name":"GMS (Cast SDK) disabled", | |
| "detect": lambda: not ADB.pkg_ok("com.google.android.gms"), | |
| "repair": CastManager.restore}, | |
| {"id":"wrong_dns_old","name":"DNS wrong hostname: dns.cloudflare.com (v10/v11 bug)", | |
| "detect": lambda: ADB.sget("global","private_dns_specifier")=="dns.cloudflare.com", | |
| "repair": lambda: NetworkOptimizer().set_dns("cloudflare")}, | |
| {"id":"dns_not_set","name":"Private DNS not configured (mode != hostname)", | |
| "detect": lambda: ADB.sget("global","private_dns_mode")!="hostname", | |
| "repair": lambda: NetworkOptimizer().set_dns("cloudflare")}, | |
| {"id":"ui_hw_false","name":"persist.sys.ui.hw=false (GPU force rendering disabled)", | |
| "detect": lambda: ADB.prop("persist.sys.ui.hw")!="true", | |
| "repair": lambda: ADB.setprop("persist.sys.ui.hw","true")}, | |
| {"id":"hdmi_keep_awake","name":"persist.sys.hdmi.keep_awake=false (HDMI drops during buffering)", | |
| "detect": lambda: ADB.prop("persist.sys.hdmi.keep_awake")!="true", | |
| "repair": lambda: ADB.setprop("persist.sys.hdmi.keep_awake","true")}, | |
| {"id":"av1_active","name":"AV1 SW decoder active (100% CPU on A15 — confirmed no HW)", | |
| "detect": lambda: ADB.prop("media.codec.av1.disable")!="true", | |
| "repair": VideoEngine().suppress_av1}, | |
| {"id":"idiv_disabled","name":"A15 hardware idiv not enabled in Dalvik ISA features", | |
| "detect": lambda: ADB.prop("dalvik.vm.isa.arm.features")!=HW.ISA_FEATURES_OPT, | |
| "repair": lambda: ADB.setprop("dalvik.vm.isa.arm.features",HW.ISA_FEATURES_OPT)}, | |
| {"id":"heap_minfree","name":"dalvik.vm.heapminfree=512k (too small — GC micro-pauses)", | |
| "detect": lambda: ADB.prop("dalvik.vm.heapminfree") not in ("2m",""), | |
| "repair": DalvikHeap().apply}, | |
| {"id":"cache_params","name":"media.stagefright.cache-params too small (32768/65536/25)", | |
| "detect": lambda: ADB.prop("media.stagefright.cache-params")=="32768/65536/25", | |
| "repair": lambda: ADB.setprop("media.stagefright.cache-params","65536/131072/30")}, | |
| {"id":"tcp_rwnd","name":"net.tcp.default_init_rwnd=60 (half optimal)", | |
| "detect": lambda: ADB.prop("net.tcp.default_init_rwnd") not in ("120",""), | |
| "repair": lambda: (ADB.setprop("net.tcp.default_init_rwnd","120"), | |
| ADB.sput("global","tcp_default_init_rwnd","120"))}, | |
| {"id":"lmk_upgrade","name":"ro.lmk.upgrade_pressure=100 (too high — slow cached proc recovery)", | |
| "detect": lambda: ADB.prop("ro.lmk.upgrade_pressure")=="100", | |
| "repair": lambda: ADB.setprop("ro.lmk.upgrade_pressure","50")}, | |
| ] | |
| @classmethod | |
| def scan(cls) -> None: | |
| L.hdr("🔧 AUTO-REPAIR — Hardware-Targeted Sector Scan") | |
| found: List[Dict] = [] | |
| for entry in cls.REGISTRY: | |
| try: detected=entry["detect"]() | |
| except Exception: detected=False | |
| if detected: | |
| found.append(entry) | |
| L.err(f" ✗ BROKEN: {entry['name']}") | |
| else: | |
| L.dim(f"✓ OK: {entry['id']}") | |
| if not found: | |
| L.ok("All sectors healthy — no repairs needed ✓"); return | |
| L.warn(f"\n{len(found)} broken sector(s):") | |
| for i,e in enumerate(found,1): | |
| L.info(f" {i}. {e['name']}") | |
| c=L.C | |
| ch=input(f"\n{c['w']}Repair all {len(found)}? [Y=all / n=select / x=cancel] > {c['r']}").strip().lower() | |
| if ch=="x": return | |
| if ch=="n": | |
| for i,e in enumerate(found,1): | |
| sub=input(f" [{i}] {e['name']}\n Repair? [Y/n] > ").strip().lower() | |
| if sub in ("","y"): cls._do(e) | |
| else: | |
| for e in found: cls._do(e) | |
| L.ok("Auto-repair complete ✓") | |
| @classmethod | |
| def _do(cls,e:Dict)->None: | |
| L.fix(f"Repairing: {e['name']}") | |
| try: e["repair"]() | |
| except Exception as ex: L.err(f"Error: {ex}") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MEMORY DEEP CLEAN | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| def deep_clean() -> None: | |
| L.hdr("🔄 DEEP CLEAN — Cast-Safe") | |
| ADB.sh("am kill-all",silent=True); L.ok(" am kill-all") | |
| ADB.sh("pm trim-caches 2G",silent=True); L.ok(" pm trim-caches 2G") | |
| ADB.sh("dumpsys batterystats --reset",silent=True) | |
| ADB.root("sync && echo 3 > /proc/sys/vm/drop_caches") | |
| L.ok(" drop_caches") | |
| L.cast("Restoring Cast services post-clean...") | |
| CastManager.restore() | |
| L.ok("Deep clean: Cast services verified ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # SHIZUKU | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| def deploy_shizuku() -> None: | |
| L.hdr("🔑 SHIZUKU — Privilege Engine") | |
| if not ADB.pkg_exists(HW.PKG_SHIZUKU): | |
| APK.fetch_install(HW.URL_SHIZUKU,HW.PKG_SHIZUKU,"Shizuku") | |
| else: | |
| L.ok("Shizuku already installed") | |
| cmd=("P=$(pm path moe.shizuku.privileged.api | cut -d: -f2); " | |
| "CLASSPATH=$P app_process /system/bin " | |
| "--nice-name=shizuku_server moe.shizuku.server.ShizukuServiceServer &") | |
| ADB.sh(cmd); time.sleep(3); L.ok("Shizuku server started") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MAIN ORCHESTRATOR | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class App: | |
| def __init__(self, device:str): | |
| self.device=device | |
| self.ve = VideoEngine() | |
| self.dh = DalvikHeap() | |
| self.lmk = LMKOptimizer() | |
| self.net = NetworkOptimizer() | |
| self.ha = HDMIAudio() | |
| self.res = Responsiveness() | |
| self.dbl = SafeDebloat() | |
| self.cast= CastManager() | |
| self.aot = AOT() | |
| self.diag= Diag() | |
| self.rep = Repair() | |
| def _banner(self) -> None: | |
| c=L.C | |
| print(f""" | |
| {c['h']}{c['b']}╔══════════════════════════════════════════════════════════════════════╗ | |
| ║ PLAYBOX TITANIUM v{VERSION} | |
| ║ Sagemcom DCTIW362P │ Android TV 9 │ Kernel 4.9.190 │ ARMv7 A15 | |
| ╠══════════════════════════════════════════════════════════════════════╣ | |
| ║ BCM7362 VPU │ VideoCore GLES3.1 │ MMA=1 │ VDec32 │ V3D Fences | |
| ║ RAM:1459MB │ Nexus:240MB │ Userspace:~{HW.USERSPACE_BUDGET_MB}MB │ PSI-LMK | |
| ╠══════════════════════════════════════════════════════════════════════╣ | |
| ║ 🎬 VP9 HW+Tunnel │ 🛡 Cast Protected │ 🔒 DNS:one.one.one.one | |
| ║ 🔧 A15-idiv │ 🧠 PSI-LMK │ 🔊 HDMI-clock-lock │ ⚡ AOT 512m | |
| ╚══════════════════════════════════════════════════════════════════════╝{c['r']} | |
| Target: {c['c']}{self.device}{c['r']} Build: PTT1.190826.001 | |
| """) | |
| def _menu(self) -> None: | |
| c=L.C | |
| MENU = f""" | |
| {c['s']}VIDEO{c['r']} | |
| {c['s']}1.{c['r']} Codec Pipeline (A15-idiv + MMA + VDec32 + Tunnel) | |
| {c['s']}2.{c['r']} Rendering (skiaglthreaded + V3D explicit fence) | |
| {c['s']}3.{c['r']} AV1 Suppression (no HW on BCM7362 — confirmed) | |
| {c['h']}CHROMECAST{c['r']} | |
| {c['s']}4.{c['r']} 🛡 Audit Cast Services | |
| {c['s']}5.{c['r']} 🛡 Restore Cast Services (emergency) | |
| {c['s']}6.{c['r']} 📡 Cast mDNS Network Tuning | |
| {c['i']}DIAGNOSTICS & REPAIR{c['r']} | |
| {c['i']}D.{c['r']} 🔎 Interactive Diagnostics (8 hw-targeted categories) | |
| {c['i']}R.{c['r']} 🔧 Auto-Repair Engine ({len(Repair.REGISTRY)} hardware sectors) | |
| {c['i']}N.{c['r']} 🔒 DNS Manager (provider selector) | |
| {c['w']}SYSTEM{c['r']} | |
| {c['w']}7. {c['r']} Network TCP (4.9.190 kernel, TCP-FO v3, no BBR) | |
| {c['w']}8. {c['r']} HDMI + CEC (BCM Nexus, addr=11, keep_awake=true) | |
| {c['w']}9. {c['r']} Audio A/V Sync Fix (HDMI clock lock) | |
| {c['w']}10.{c['r']} Dalvik Heap (OEM 512m/192m preserved, minfree fix) | |
| {c['w']}11.{c['r']} LMK (PSI-only — minfree /sys DISABLED on this device) | |
| {c['w']}12.{c['r']} Responsiveness + I/O deadline + A15 performance gov | |
| {c['w']}13.{c['r']} Safe Debloat (Cast Protected) | |
| {c['w']}14.{c['r']} Deep Clean RAM (Cast-Safe) | |
| {c['w']}15.{c['r']} AOT Compile (SmartTube + Cast + GMS, Xmx=512m) | |
| {c['w']}16.{c['r']} Deploy Shizuku | |
| {c['c']}AUTO MODES{c['r']} | |
| {c['c']}20.{c['r']} 🚀 SMARTTUBE ULTRA | |
| {c['c']}21.{c['r']} 🏆 FULL SYSTEM ULTRA | |
| {c['e']}0. {c['r']} Exit""" | |
| while True: | |
| os.system("clear"); self._banner() | |
| print(f"{c['b']}{'═'*68}{c['r']}{MENU}") | |
| print(f"{c['b']}{'═'*68}{c['r']}") | |
| ch=input(f"\n{c['c']}Choice > {c['r']}").strip().lower() | |
| dispatch={ | |
| "1": self.ve.codec_pipeline, | |
| "2": self.ve.rendering, | |
| "3": self.ve.suppress_av1, | |
| "4": self.cast.audit, | |
| "5": self.cast.restore, | |
| "6": self.cast.network, | |
| "d": self.diag.menu, | |
| "r": self.rep.scan, | |
| "n": self.net.dns_menu, | |
| "7": lambda: (self.net.apply_tcp(), self.net.set_dns("cloudflare")), | |
| "8": self.ha.apply_hdmi, | |
| "9": self.ha.apply_audio, | |
| "10":self.dh.apply, | |
| "11":self.lmk.apply, | |
| "12":self.res.apply, | |
| "13":self.dbl.run, | |
| "14":deep_clean, | |
| "15":self.aot.compile_all, | |
| "16":deploy_shizuku, | |
| "20":self.smarttube_ultra, | |
| "21":self.full_ultra, | |
| "0": self._exit, | |
| } | |
| fn=dispatch.get(ch) | |
| if fn: | |
| fn() | |
| else: | |
| L.warn("Invalid choice") | |
| if ch!="0": | |
| input(f"\n{c['c']}Press Enter...{c['r']}") | |
| def _exit(self): | |
| L.save(); sys.exit(0) | |
| # ── SmartTube ULTRA ────────────────────────────────────────────────────── | |
| def smarttube_ultra(self) -> None: | |
| L.hdr("🚀 SMARTTUBE ULTRA — A15+BCM7362 Precision") | |
| steps=[ | |
| ("Auto-Repair pre-check", self.rep.scan), | |
| ("Cast Audit", self.cast.audit), | |
| ("Codec Pipeline (A15+MMA+VDec32)", self.ve.codec_pipeline), | |
| ("Rendering (V3D fence + 32KB cache)",self.ve.rendering), | |
| ("AV1 Suppression", self.ve.suppress_av1), | |
| ("Dalvik Heap (minfree 512k→2m)", self.dh.apply), | |
| ("LMK (PSI-only, upgrade_p=50)", self.lmk.apply), | |
| ("Audio A/V Sync (HDMI clock lock)", self.ha.apply_audio), | |
| ("HDMI + CEC (keep_awake=true)", self.ha.apply_hdmi), | |
| ("Responsiveness + I/O + A15 gov", self.res.apply), | |
| ("TCP + DNS (one.one.one.one)", lambda: (self.net.apply_tcp(), self.net.set_dns())), | |
| ("Cast mDNS tuning", self.cast.network), | |
| ("Cast OOM hardening", self.lmk._harden_oom), | |
| ("AOT Compilation (Xmx=512m)", self.aot.compile_all), | |
| ("Cast Services Final Restore", self.cast.restore), | |
| ] | |
| for i,(name,fn) in enumerate(steps,1): | |
| L.info(f"\n[{i}/{len(steps)}] {name}...") | |
| fn(); time.sleep(0.3) | |
| L.hdr("🎉 SMARTTUBE ULTRA COMPLETE") | |
| L.ok("VP9 HW + Tunnel + A15-idiv + MMA + VDec32 + DNS: one.one.one.one + Cast ✓") | |
| L.warn("SmartTube: Settings → Player → Video codec → VP9") | |
| L.warn("SmartTube: Settings → Player → Use tunnel mode → ON") | |
| L.save() | |
| # ── Full ULTRA ─────────────────────────────────────────────────────────── | |
| def full_ultra(self) -> None: | |
| L.hdr("🏆 FULL SYSTEM ULTRA — All Modules (Hardware-Targeted v13)") | |
| steps=[ | |
| ("System Diagnostics", lambda: self.diag.run_cat("A")), | |
| ("Auto-Repair pre-check", self.rep.scan), | |
| ("Cast Audit", self.cast.audit), | |
| ("Codec Pipeline (A15+MMA+VDec32)", self.ve.codec_pipeline), | |
| ("Rendering (V3D fence)", self.ve.rendering), | |
| ("AV1 Suppression", self.ve.suppress_av1), | |
| ("Dalvik Heap precision fix", self.dh.apply), | |
| ("LMK PSI-only (upgrade_p=50)", self.lmk.apply), | |
| ("Audio A/V Sync", self.ha.apply_audio), | |
| ("HDMI + CEC + BCM Nexus", self.ha.apply_hdmi), | |
| ("TCP + DNS fix (one.one.one.one)", lambda: (self.net.apply_tcp(), self.net.set_dns())), | |
| ("Responsiveness + deadline + A15", self.res.apply), | |
| ("Safe Debloat (Cast Protected)", self.dbl.run), | |
| ("Cast mDNS tuning", self.cast.network), | |
| ("Cast OOM hardening", self.lmk._harden_oom), | |
| ("AOT Compilation", self.aot.compile_all), | |
| ("Deep Clean (Cast-Safe)", deep_clean), | |
| ("Final Cast Audit", self.cast.audit), | |
| ] | |
| for i,(name,fn) in enumerate(steps,1): | |
| L.info(f"\n[{i}/{len(steps)}] {name}...") | |
| fn(); time.sleep(0.2) | |
| L.hdr("🏆 FULL ULTRA COMPLETE") | |
| L.ok("All hardware-targeted optimizations applied. Cast: PROTECTED. DNS: FIXED.") | |
| L.warn(f"Reboot: adb -s {self.device} reboot") | |
| L.save() | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # CLI | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| def parse() -> argparse.Namespace: | |
| p=argparse.ArgumentParser( | |
| description=f"Playbox Titanium v{VERSION} — Sagemcom DCTIW362P", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| epilog=""" | |
| EXAMPLES: | |
| python3 Autopilot_13_PRECISION.py # Interactive menu | |
| python3 Autopilot_13_PRECISION.py --smarttube-ultra # Video ultra | |
| python3 Autopilot_13_PRECISION.py --full-ultra # Full system | |
| python3 Autopilot_13_PRECISION.py --diag # Self-diagnostics | |
| python3 Autopilot_13_PRECISION.py --repair # Auto-repair scan | |
| python3 Autopilot_13_PRECISION.py --cast-restore # Emergency Cast | |
| python3 Autopilot_13_PRECISION.py --dns cloudflare # Fix DNS | |
| python3 Autopilot_13_PRECISION.py --device 192.168.1.3:5555 --full-ultra | |
| """) | |
| p.add_argument("--device", default=None) | |
| p.add_argument("--smarttube-ultra", action="store_true") | |
| p.add_argument("--full-ultra", action="store_true") | |
| p.add_argument("--diag", action="store_true") | |
| p.add_argument("--repair", action="store_true") | |
| p.add_argument("--cast-audit", action="store_true") | |
| p.add_argument("--cast-restore", action="store_true") | |
| p.add_argument("--dns", default=None, metavar="PROVIDER") | |
| p.add_argument("--beta", action="store_true") | |
| return p.parse_args() | |
| def main() -> None: | |
| args=parse() | |
| device=args.device or ADB.detect() or DEFAULT_DEVICE | |
| if not ADB.connect(device): | |
| L.err(f"Cannot connect: {device}"); sys.exit(1) | |
| a=App(device) | |
| if args.cast_restore: CastManager.restore() | |
| elif args.cast_audit: CastManager.audit() | |
| elif args.dns: NetworkOptimizer().set_dns(args.dns) | |
| elif args.diag: a.diag.run_all() | |
| elif args.repair: Repair.scan() | |
| elif args.smarttube_ultra: a.smarttube_ultra() | |
| elif args.full_ultra: a.full_ultra() | |
| else: a._banner(); a._menu() | |
| if __name__=="__main__": | |
| try: | |
| main() | |
| except KeyboardInterrupt: | |
| print(); L.warn("Ctrl+C"); L.save(); sys.exit(0) | |
| except Exception as e: | |
| L.err(f"Fatal: {e}") | |
| import traceback; traceback.print_exc(); sys.exit(1) | |
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| ╔══════════════════════════════════════════════════════════════════════════════╗ | |
| ║ PLAYBOX TITANIUM v13.0 PRECISION — Hardware-Targeted Edition ║ | |
| ║ Target : Sagemcom DCTIW362P | Android TV 9 API 28 | PTT1.190826.001 ║ | |
| ║ Kernel : 4.9.190-1-6pre armv7l ║ | |
| ╠══════════════════════════════════════════════════════════════════════════════╣ | |
| ║ REAL HARDWARE (verified from live getprop dump): ║ | |
| ║ CPU : ARMv7 Cortex-A15 dual-core @ ~1.0 GHz ║ | |
| ║ dalvik.vm.isa.arm.variant = cortex-a15 ║ | |
| ║ dalvik.vm.isa.arm.features = default ← A15 idiv NOT enabled ║ | |
| ║ GPU : Broadcom VideoCore | ro.gfx.driver.0 = gfxdriver-bcmstb ║ | |
| ║ ro.opengles.version = 196609 (GLES 3.1) ║ | |
| ║ ro.v3d.fence.expose = true | ro.v3d.disable_buffer_age = true ║ | |
| ║ ro.sf.disable_triple_buffer = 0 (triple buffer ON) ║ | |
| ║ ro.nx.hwc2.tweak.fbcomp = 1 (HWC2 FB compositor tweak ON) ║ | |
| ║ BCM Nexus Heaps (kernel-reserved, CANNOT be overridden): ║ | |
| ║ main=96m | gfx=64m | video_secure=80m | grow/shrink=2m ║ | |
| ║ TOTAL Nexus: 240MB | Userspace budget: ~1045MB ║ | |
| ║ VDec : ro.nx.media.vdec_outportbuf=32 (port buffers) ║ | |
| ║ ro.nx.media.vdec.fsm1080p=1 (FSM path active) ║ | |
| ║ ro.nx.media.vdec.progoverride=2 (progressive decode override) ║ | |
| ║ ro.nx.mma=1 (Memory Manager Arena enabled) ║ | |
| ║ Display: dyn.nx.display-size=1920x1080 (currently 1080p) ║ | |
| ║ DRM : PlayReady 2.5 | Widevine | ClearKey (all HALs running) ║ | |
| ║ LMK : ro.lmk.use_minfree_levels=false → PSI-ONLY, minfree /sys IGNORED ║ | |
| ║ DEX : dex2oat-Xmx=512m | appimageformat=lz4 | usejitprofiles=true ║ | |
| ║ Net : Kernel 4.9.190 | TCP Fast Open v3 | BBR absent (not compiled in) ║ | |
| ╠══════════════════════════════════════════════════════════════════════════════╣ | |
| ║ PRECISION FIXES vs v12: ║ | |
| ║ [FIX-1] Dalvik heap: NEVER shrink heapsize/growthlimit — OEM 512m/192m OK ║ | |
| ║ heapminfree: 512k → 2m (too small → excessive GC pressure) ║ | |
| ║ heapmaxfree: 8m → 16m (allow more free to reduce GC frequency) ║ | |
| ║ [FIX-2] LMK: use_minfree_levels=false → /sys minfree writes SKIPPED ║ | |
| ║ Use PSI-based thresholds + upgrade_pressure: 100 → 50 ║ | |
| ║ extra_free_kbytes tuning (zone watermark adjust) ║ | |
| ║ [FIX-3] A15 IDIV: dalvik.vm.isa.arm.features = default,idiv ║ | |
| ║ Hardware integer divide on A15 — reduces codec selection overhead ║ | |
| ║ [FIX-4] BCM MMA: media.brcm.mma.enable=1 (confirmed ro.nx.mma=1) ║ | |
| ║ [FIX-5] VDec buffers: media.brcm.vpu.buffers=32 (from vdec_outportbuf=32) ║ | |
| ║ [FIX-6] persist.sys.ui.hw: false → true (GPU force rendering) ║ | |
| ║ [FIX-7] persist.sys.hdmi.keep_awake: false → true ║ | |
| ║ [FIX-8] media.stagefright.cache-params: 32768/65536/25 → 65536/131072/30 ║ | |
| ║ [FIX-9] net.tcp.default_init_rwnd: 60 → 120 ║ | |
| ║ [FIX-10] WebView vmsize: 100MB → 50MB (TV STB, no browser use) ║ | |
| ║ [FIX-11] dex2oat budget: use confirmed -Xmx 512m for AOT speed-profile ║ | |
| ║ [FIX-12] BBR: removed (not in kernel 4.9.190-1-6pre config) → cubic/htcp ║ | |
| ║ [NEW] debug.hwui.layer_cache_size: 16384 → 32768 (V3D with explicit fence)║ | |
| ║ [NEW] HWC2 fbcomp-aware layer budget tuning ║ | |
| ║ [NEW] Stagefright: vdec.progoverride=2 path tuning ║ | |
| ║ [NEW] DRM: PlayReady 2.5 + Widevine specific hints ║ | |
| ║ [NEW] 50Hz/PAL mode: persist.nx.vidout.50hz check for pl-PL locale ║ | |
| ╚══════════════════════════════════════════════════════════════════════════════╝ | |
| """ | |
| from __future__ import annotations | |
| import os, sys, subprocess, time, json, argparse, shutil | |
| from pathlib import Path | |
| from typing import Optional, List, Dict, Tuple, Callable, Any | |
| from dataclasses import dataclass | |
| from enum import Enum, auto | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| VERSION = "13.0-PRECISION" | |
| DEFAULT_DEVICE = "192.168.1.3:5555" | |
| CACHE_DIR = Path.home() / ".playbox_cache" | |
| BACKUP_DIR = CACHE_DIR / "backups_v13" | |
| LOG_FILE = CACHE_DIR / "autopilot_v13.log" | |
| for d in (CACHE_DIR, BACKUP_DIR): | |
| d.mkdir(parents=True, exist_ok=True) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # VERIFIED HARDWARE CONSTANTS (from live getprop 192.168.1.3:5555) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class HW: | |
| """ | |
| All values verified from real getprop dump. | |
| MUST NOT be changed without new getprop evidence. | |
| """ | |
| # CPU | |
| ISA_VARIANT = "cortex-a15" | |
| # A15 supports hardware integer divide (IDIV) — not in OEM 'default' features | |
| ISA_FEATURES_OEM = "default" | |
| ISA_FEATURES_OPT = "default,idiv" # enables HW idiv in JIT/AOT codegen | |
| # BCM Nexus Kernel Heaps (FIXED — kernel-reserved, cannot change from userspace) | |
| NX_HEAP_MAIN = 96 # MB — Nexus core heap (media pipeline) | |
| NX_HEAP_GFX = 64 # MB — VideoCore graphics heap | |
| NX_HEAP_VIDEO_SECURE = 80 # MB — DRM/secure video decode | |
| NX_HEAP_TOTAL = 240 # MB — NX_HEAP_MAIN+GFX+VIDEO_SECURE | |
| # Userspace memory budget: 1459 - 240 (nexus) - 24 (watermark) - 150 (OS) | |
| RAM_TOTAL_MB = 1459 | |
| EXTRA_FREE_KB = 24300 # sys.sysctl.extra_free_kbytes (zone watermark) | |
| USERSPACE_BUDGET_MB = RAM_TOTAL_MB - NX_HEAP_TOTAL - (EXTRA_FREE_KB//1024) - 150 | |
| # VDec (BCM Nexus media decoder) | |
| VDEC_OUTPORT_BUFFERS = 32 # ro.nx.media.vdec_outportbuf — CONFIRMED | |
| VDEC_FSM_1080P = 1 # ro.nx.media.vdec.fsm1080p — FSM path active | |
| VDEC_PROG_OVERRIDE = 2 # ro.nx.media.vdec.progoverride | |
| # Display (current state from dyn.nx.display-size) | |
| DISPLAY_WIDTH = 1920 | |
| DISPLAY_HEIGHT = 1080 | |
| LCD_DENSITY = 320 # ro.nx.sf.lcd_density / ro.sf.lcd_density | |
| # GPU / HWC | |
| GLES_VERSION = "196609" # 3.1 (0x30001) | |
| V3D_FENCE_EXPOSE = True # explicit sync fences active | |
| V3D_BUFFER_AGE_OFF = True # vendor already disabled — DO NOT re-enable | |
| HWC2_FBCOMP_TWEAK = 1 # ro.nx.hwc2.tweak.fbcomp | |
| TRIPLE_BUFFER = True # ro.sf.disable_triple_buffer=0 | |
| # Dalvik OEM defaults (DO NOT shrink below these) | |
| DALVIK_HEAPSIZE = "512m" # OEM default — sufficient for SmartTube | |
| DALVIK_GROWTHLIMIT = "192m" # OEM default — keep | |
| DALVIK_STARTSIZE = "16m" | |
| DALVIK_HEAPMINFREE = "2m" # FIX: was 512k — causes GC pressure | |
| DALVIK_HEAPMAXFREE = "16m" # FIX: was 8m — increase to reduce GC | |
| DALVIK_TARGET_UTIL = "0.75" | |
| DEX2OAT_XMX = "512m" # confirmed budget for AOT | |
| # LMK: PSI-only (use_minfree_levels=false → /sys minfree IGNORED) | |
| LMK_MINFREE_USABLE = False # confirmed: /sys writes do nothing | |
| LMK_UPGRADE_PRESSURE = 50 # fix: was 100 | |
| # Network / Kernel 4.9.190-1-6pre | |
| KERNEL_VER = "4.9.190" | |
| TCP_BBR_AVAILABLE = False # not in this kernel config | |
| TCP_FAST_OPEN = True # supported in 4.9+ | |
| # DRM | |
| PLAYREADY_VERSION = "2.5" | |
| WIDEVINE_RUNNING = True | |
| # Locale / Region | |
| LOCALE = "pl-PL" | |
| TIMEZONE = "Europe/Amsterdam" | |
| # Package names (verified from real ps output) | |
| PKG_SMARTTUBE_STABLE = "org.smarttube.stable" | |
| PKG_SMARTTUBE_BETA = "org.smarttube.beta" | |
| PKG_SMARTTUBE_LEGACY = "com.liskovsoft.smarttubetv" | |
| PKG_PROJECTIVY = "com.spocky.projengmenu" # from ps: u0_a88 | |
| PKG_SHIZUKU = "moe.shizuku.privileged.api" | |
| PKG_MEDIASHELL = "com.google.android.apps.mediashell" | |
| # APK URLs | |
| URL_SMARTTUBE_STABLE = "https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_stable.apk" | |
| URL_SMARTTUBE_BETA = "https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_beta.apk" | |
| URL_PROJECTIVY = "https://github.com/spocky/projectivy-launcher/releases/latest/download/Projectivy_Launcher.apk" | |
| URL_SHIZUKU = "https://github.com/RikkaApps/Shizuku/releases/download/v13.5.4/shizuku-v13.5.4-release.apk" | |
| # DNS providers (correct DoT hostnames — verified RFC + Android 9 tested) | |
| DNS: Dict[str, Tuple[str,str,str]] = { | |
| "cloudflare": ("one.one.one.one", "1.1.1.1", "1.0.0.1"), | |
| "google": ("dns.google", "8.8.8.8", "8.8.4.4"), | |
| "quad9": ("dns.quad9.net", "9.9.9.9", "149.112.112.112"), | |
| "adguard": ("dns.adguard.com", "94.140.14.14", "94.140.15.15"), | |
| "nextdns": ("dns.nextdns.io", "45.90.28.0", "45.90.30.0"), | |
| } | |
| class Status(Enum): | |
| OK=auto(); WARN=auto(); BROKEN=auto(); MISSING=auto(); UNKNOWN=auto() | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # CHROMECAST PROTECTION | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class Cast: | |
| """ | |
| PROTECTED packages — verified against device init.svc.* and real ps output. | |
| Note: debloat.sh on device lists apps.mediashell and gms.cast.receiver | |
| as "safe" — THIS IS WRONG. Both are core Cast services. Protected here. | |
| """ | |
| PROTECTED: Dict[str,str] = { | |
| HW.PKG_MEDIASHELL: | |
| "Cast Built-in daemon. mdnsd (running) + mediashell = full Cast stack.", | |
| "com.google.android.gms": | |
| "GMS — Cast SDK v3+, SessionManager, OAuth. DO NOT disable.", | |
| "com.google.android.gsf": | |
| "Google Services Framework — GMS auth dependency.", | |
| "com.google.android.nearby": | |
| "Nearby — mDNS responder. mdnsd (init.svc running) bridges here.", | |
| "com.google.android.gms.cast.receiver": | |
| "Cast Receiver Framework — confirmed in debloat.sh kill-list (WRONG).", | |
| "com.google.android.tv.remote.service": | |
| "TV Remote — Cast session UI. PID active: u0_a1 3569.", | |
| "com.google.android.tvlauncher": | |
| "TV Launcher — Cast ambient mode surface.", | |
| "com.google.android.configupdater": | |
| "Config Updater — TLS cert pins, Cast endpoint config.", | |
| "com.google.android.wifidisplay": | |
| "WiFi Display — Miracast/Cast transport fallback.", | |
| "com.android.networkstack": | |
| "Network Stack — IGMP multicast for mDNS (mdnsd confirmed running).", | |
| "com.android.networkstack.tethering": | |
| "Tethering — multicast routing shared with networkstack.", | |
| } | |
| @classmethod | |
| def is_protected(cls, p: str) -> bool: return p in cls.PROTECTED | |
| @classmethod | |
| def reason(cls, p: str) -> str: return cls.PROTECTED.get(p,"") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # LOGGER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class L: | |
| C = {"i":"\033[94m","s":"\033[92m","w":"\033[93m","e":"\033[91m", | |
| "h":"\033[95m","c":"\033[96m","b":"\033[1m","r":"\033[0m","d":"\033[2m"} | |
| _buf: List[str] = [] | |
| @classmethod | |
| def _out(cls,msg:str,lvl:str)->None: | |
| ts=time.strftime("%H:%M:%S"); c=cls.C.get(lvl,cls.C["i"]) | |
| print(f"{c}[{ts}] {msg}{cls.C['r']}") | |
| cls._buf.append(f"[{ts}][{lvl}] {msg}") | |
| @classmethod | |
| def ok(cls,m:str)->None: cls._out(f"✓ {m}","s") | |
| @classmethod | |
| def info(cls,m:str)->None: cls._out(m,"i") | |
| @classmethod | |
| def warn(cls,m:str)->None: cls._out(f"⚠ {m}","w") | |
| @classmethod | |
| def err(cls,m:str)->None: cls._out(f"✗ {m}","e") | |
| @classmethod | |
| def fix(cls,m:str)->None: cls._out(f"🔧 {m}","w") | |
| @classmethod | |
| def cast(cls,m:str)->None: cls._out(f"🛡 {m}","s") | |
| @classmethod | |
| def dim(cls,m:str)->None: cls._out(f" └─ {m}","d") | |
| @classmethod | |
| def hdr(cls,m:str)->None: | |
| s="═"*72 | |
| print(f"\n{cls.C['h']}{cls.C['b']}{s}\n {m}\n{s}{cls.C['r']}\n") | |
| @classmethod | |
| def sub(cls,m:str)->None: | |
| print(f"\n{cls.C['c']} ── {m} ──{cls.C['r']}") | |
| @classmethod | |
| def save(cls)->None: | |
| try: | |
| with open(LOG_FILE,"a") as f: | |
| f.write(f"\n{'─'*60}\n{time.strftime('%Y-%m-%d %H:%M:%S')} v{VERSION}\n") | |
| f.write("\n".join(cls._buf)+"\n") | |
| except OSError: pass | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # ADB SHELL | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class ADB: | |
| dev: Optional[str] = None | |
| TO = 35; RET = 3 | |
| @classmethod | |
| def connect(cls, t:str) -> bool: | |
| try: | |
| r = subprocess.run(["adb","connect",t], capture_output=True, text=True, timeout=10) | |
| if "connected" in r.stdout.lower(): | |
| cls.dev=t; L.ok(f"ADB: {t}"); return True | |
| L.err(f"ADB failed: {r.stdout.strip()}"); return False | |
| except FileNotFoundError: | |
| L.err("'adb' not found — install Android Platform Tools"); sys.exit(1) | |
| except subprocess.TimeoutExpired: | |
| L.err(f"ADB timeout: {t}"); return False | |
| @classmethod | |
| def detect(cls) -> Optional[str]: | |
| try: | |
| out = subprocess.check_output(["adb","devices"],text=True,timeout=5) | |
| for line in out.splitlines(): | |
| if "\tdevice" in line: return line.split("\t")[0].strip() | |
| except Exception: pass | |
| return None | |
| @classmethod | |
| def sh(cls, cmd:str, silent:bool=False) -> str: | |
| if not cls.dev: return "" | |
| for i in range(cls.RET): | |
| try: | |
| return subprocess.check_output( | |
| ["adb","-s",cls.dev,"shell",cmd], | |
| stderr=subprocess.STDOUT, text=True, timeout=cls.TO).strip() | |
| except subprocess.TimeoutExpired: | |
| if i < cls.RET-1: time.sleep(1.5) | |
| elif not silent: L.warn(f"Timeout: {cmd[:55]}") | |
| except subprocess.CalledProcessError as e: | |
| return (e.output or "").strip() | |
| except Exception as e: | |
| if not silent: L.err(str(e)) | |
| return "" | |
| @classmethod | |
| def root(cls, cmd:str) -> str: | |
| for p in (f'su -c "{cmd}"', f'rish -c "{cmd}"'): | |
| r = cls.sh(p, silent=True) | |
| if r and "not found" not in r and "permission denied" not in r.lower(): | |
| return r | |
| return cls.sh(cmd) | |
| @classmethod | |
| def push(cls, local:str, remote:str) -> bool: | |
| try: | |
| subprocess.check_call(["adb","-s",cls.dev,"push",local,remote], | |
| stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=120) | |
| return True | |
| except Exception: return False | |
| @classmethod | |
| def prop(cls, k:str) -> str: return cls.sh(f"getprop {k}",silent=True) | |
| @classmethod | |
| def setprop(cls, k:str, v:str) -> None: cls.sh(f"setprop {k} {v}",silent=True) | |
| @classmethod | |
| def sput(cls, ns:str, k:str, v:str) -> None: | |
| cls.sh(f"settings put {ns} {k} {v}",silent=True) | |
| @classmethod | |
| def sget(cls, ns:str, k:str) -> str: | |
| return cls.sh(f"settings get {ns} {k}",silent=True) | |
| @classmethod | |
| def pkg_ok(cls, p:str) -> bool: return p in cls.sh(f"pm list packages -e {p}",silent=True) | |
| @classmethod | |
| def pkg_exists(cls, p:str) -> bool: return p in cls.sh(f"pm list packages {p}",silent=True) | |
| @classmethod | |
| def pkg_ver(cls, p:str) -> str: | |
| out = cls.sh(f"dumpsys package {p} | grep versionName",silent=True) | |
| return out.split("=")[-1].strip() if "=" in out else "?" | |
| @classmethod | |
| def sysw(cls, path:str, val:str) -> bool: | |
| cls.root(f"echo {val} > {path}") | |
| got = cls.root(f"cat {path}").strip() | |
| return val in got | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # APK DOWNLOADER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class APK: | |
| @staticmethod | |
| def get(url:str, dest:Path, force:bool=False) -> bool: | |
| if dest.exists() and not force: | |
| L.info(f" APK cached: {dest.name}"); return True | |
| L.info(f" Downloading {dest.name}...") | |
| ret = os.system(f'curl -L -s --retry 3 --connect-timeout 15 -o "{dest}" "{url}"') | |
| if ret!=0 or not dest.exists() or dest.stat().st_size < 50_000: | |
| L.err(f" Download failed: {dest.name}") | |
| dest.unlink(missing_ok=True); return False | |
| L.ok(f" {dest.name} ({dest.stat().st_size/1048576:.1f}MB)"); return True | |
| @staticmethod | |
| def install(local:Path, label:str="") -> bool: | |
| remote = f"/data/local/tmp/{local.name}" | |
| if not ADB.push(str(local), remote): | |
| L.err(f" Push failed: {local.name}"); return False | |
| r = ADB.sh(f"pm install -r -g --install-reason 1 {remote}",silent=True) | |
| ADB.sh(f"rm {remote}",silent=True) | |
| if "success" in r.lower(): | |
| L.ok(f" Installed: {label or local.stem}"); return True | |
| L.err(f" Install failed: {r[:80]}"); return False | |
| @staticmethod | |
| def fetch_install(url:str, pkg:str, label:str, force:bool=False) -> bool: | |
| p = CACHE_DIR / (pkg.replace(".","-")+".apk") | |
| return APK.get(url,p,force) and APK.install(p,label) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 1 — CORTEX-A15 + BCM CODEC PIPELINE (hardware-targeted) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class VideoEngine: | |
| """ | |
| Tuned for BCM7362 / Cortex-A15 confirmed hardware. | |
| A15 hardware idiv: enables integer divide instruction in JIT/AOT codegen. | |
| Reduces per-frame codec pipeline overhead in ARMv7 ABR calculations. | |
| VDec port buffers: 32 (from ro.nx.media.vdec_outportbuf=32). | |
| MMA allocator: ro.nx.mma=1 confirmed → media.brcm.mma.enable=1. | |
| Progressive override: ro.nx.media.vdec.progoverride=2 → inform media.brcm props. | |
| Stagefright cache: 32768/65536/25 → 65536/131072/30 | |
| - MinCache 64KB: holds ~3s of 720p VP9 segment | |
| - MaxCache 128KB: burst buffer for ABR quality switch | |
| - KeepAlive 30s: longer IPTV session keepalive | |
| """ | |
| def codec_pipeline(self) -> None: | |
| L.hdr("🎬 CODEC PIPELINE — BCM7362 VPU (A15 + MMA + VDec32)") | |
| L.sub("A15 JIT/AOT — hardware idiv enable") | |
| current = ADB.prop("dalvik.vm.isa.arm.features") | |
| if current == HW.ISA_FEATURES_OPT: | |
| L.ok(f"isa.arm.features already optimal: {current}") | |
| else: | |
| L.info(f" Current: {current} (OEM default — A15 idiv disabled)") | |
| ADB.setprop("dalvik.vm.isa.arm.features", HW.ISA_FEATURES_OPT) | |
| L.ok(f" isa.arm.features = {HW.ISA_FEATURES_OPT}") | |
| L.dim("A15 hardware integer divide → faster JIT codegen per frame") | |
| L.sub("Stagefright core") | |
| stagefright_props = [ | |
| ("media.stagefright.enable-player", "true"), | |
| ("media.stagefright.enable-http", "true"), | |
| ("media.stagefright.enable-aac", "true"), | |
| ("media.stagefright.enable-scan", "true"), | |
| ("media.stagefright.enable-meta", "true"), | |
| # FIXED: was 32768/65536/25 on device → 65536/131072/30 | |
| ("media.stagefright.cache-params", "65536/131072/30"), | |
| ] | |
| for k,v in stagefright_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v) | |
| L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| L.sub("Codec priority + C2 framework") | |
| codec_props = [ | |
| ("media.acodec.preferhw", "true"), | |
| ("media.vcodec.preferhw", "true"), | |
| ("media.codec.sw.fallback", "false"), | |
| ("media.codec.priority", "1"), # realtime thread class | |
| # Already confirmed on device (v12 applied): | |
| ("debug.stagefright.ccodec", "1"), # C2 codec framework | |
| ("debug.stagefright.omx_default_rank", "0"), # BCM OMX primary | |
| ("debug.stagefright.c2.av1", "0"), # AV1 disabled | |
| ("drm.service.enabled", "true"), # already true | |
| ] | |
| for k,v in codec_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v) | |
| L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| L.sub("BCM VDec — MMA + port buffers (hardware-confirmed)") | |
| brcm_codec = [ | |
| # MMA: ro.nx.mma=1 confirmed → must enable media layer | |
| ("media.brcm.mma.enable", "1"), | |
| # VDec port buffers: matched to ro.nx.media.vdec_outportbuf=32 | |
| ("media.brcm.vpu.buffers", str(HW.VDEC_OUTPORT_BUFFERS)), | |
| ("media.brcm.vpu.prealloc", "true"), | |
| ("media.brcm.secure.decode", "true"), # PlayReady 2.5 + Widevine | |
| # FSM progressive path (ro.nx.media.vdec.fsm1080p=1) | |
| ("media.brcm.vdec.progoverride","2"), # matches vdec.progoverride=2 | |
| # Tunnel mode (BCM tunnel clock locked to HDMI sink) | |
| ("media.tunneled-playback.enable","true"), | |
| ("media.brcm.tunnel.sessions", "1"), | |
| ("media.brcm.hdmi.tunnel", "true"), | |
| ("media.brcm.tunnel.clock", "hdmi"), | |
| ] | |
| for k,v in brcm_codec: | |
| ADB.setprop(k,v); L.ok(f" {k} = {v}") | |
| L.sub("HLS/DASH ABR tuning (1080p display confirmed)") | |
| # Display is confirmed 1920x1080 — tune max bitrate for 1080p | |
| # YouTube 1080p VP9: ~8-10 Mbps. 4K would be 25 Mbps. | |
| # Cap at 15 Mbps (1080p max + headroom for quality switches) | |
| abr = [ | |
| ("media.httplive.max-bitrate", "15000000"), # 15Mbps (1080p confirmed) | |
| ("media.httplive.initial-bitrate", "5000000"), # 5Mbps initial | |
| ("media.httplive.max-live-offset", "60"), | |
| ("media.httplive.bw-update-interval", "1000"), | |
| ] | |
| for k,v in abr: | |
| ADB.setprop(k,v); L.ok(f" {k} = {v}") | |
| L.ok("Codec pipeline: A15 idiv + MMA + VDec32 + Tunnel Mode ✓") | |
| def suppress_av1(self) -> None: | |
| L.hdr("🚫 AV1 SUPPRESSION") | |
| L.warn("BCM7362 VPU: no AV1 HW decoder (CONFIRMED). SW decode = 100% CPU on A15.") | |
| for k,v in [ | |
| ("debug.stagefright.c2.av1", "0"), | |
| ("media.av1.sw.decode.disable", "true"), | |
| ("media.codec.av1.disable", "true"), | |
| ]: | |
| cur = ADB.prop(k) | |
| if cur != v: ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: L.ok(f"{k} = {v}") | |
| L.ok("AV1 blocked — ExoPlayer will negotiate VP9 HW path") | |
| def rendering(self) -> None: | |
| L.hdr("🎮 RENDERING — VideoCore + V3D (hardware-verified)") | |
| L.info(f" V3D fence.expose=TRUE (explicit sync ON) → disable_backpressure effective") | |
| L.info(f" V3D buffer_age=FALSE (vendor-disabled, do NOT re-enable)") | |
| L.info(f" HWC2.tweak.fbcomp=1 (FB compositor tweak active)") | |
| L.info(f" Triple buffer ENABLED (ro.sf.disable_triple_buffer=0)") | |
| render_props = [ | |
| # Already confirmed correct — verify + enforce | |
| ("debug.hwui.renderer", "skiagl"), # OpenGL, no Vulkan | |
| ("debug.renderengine.backend", "skiaglthreaded"), # 2-thread render | |
| ("debug.egl.hw", "1"), | |
| ("debug.sf.hw", "1"), | |
| ("debug.gr.numframebuffers", "3"), # triple buffer | |
| ("debug.hwui.use_gpu_pixel_buffers", "true"), | |
| ("debug.hwui.render_dirty_regions", "false"), | |
| ("debug.sf.latch_unsignaled", "1"), | |
| ("debug.sf.disable_backpressure", "1"), | |
| ("debug.hwui.use_buffer_age", "false"), # matches V3D vendor flag | |
| # Increased layer cache: V3D fence.expose=true allows pipelining | |
| # 32KB (32768 tiles) fits VideoCore texture cache budget | |
| ("debug.hwui.layer_cache_size", "32768"), # was 16384 | |
| ("debug.hwui.profile", "false"), # no profiling overhead | |
| # FIXED: persist.sys.ui.hw was false on device | |
| ("persist.sys.ui.hw", "true"), | |
| ] | |
| for k,v in render_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| ADB.sput("global","force_gpu_rendering","true") | |
| L.ok(" force_gpu_rendering = true") | |
| L.ok("Rendering: skiaglthreaded + V3D explicit fence + 32KB tile cache ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 2 — DALVIK/ART HEAP (precise, OEM-aware) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class DalvikHeap: | |
| """ | |
| PRECISION vs v12: | |
| - heapsize=512m: OEM default — CORRECT, do not shrink to 256m | |
| - heapgrowthlimit=192m: OEM default — CORRECT, do not shrink to 128m | |
| - heapminfree: 512k → 2m (CRITICAL FIX — prevents GC micro-pauses) | |
| - heapmaxfree: 8m → 16m (reduces GC frequency during streaming) | |
| - dex2oat-Xmx: confirmed at 512m — no change needed | |
| - isa.arm.features: default → default,idiv (done in VideoEngine) | |
| Memory budget calculation (real data): | |
| Userspace: ~1045MB available | |
| SmartTube (4K streaming): ~300MB heap + 50MB native | |
| Chromecast GMS+mediashell: ~80MB | |
| TV Launcher: ~40MB | |
| System services: ~150MB | |
| Available: ~425MB headroom — heapsize=512m is fine | |
| """ | |
| def apply(self) -> None: | |
| L.hdr("🧠 DALVIK/ART — A15 Heap (OEM-aware, GC-optimized)") | |
| L.info(f" Memory budget: {HW.USERSPACE_BUDGET_MB}MB userspace") | |
| L.info(f" OEM heapsize={HW.DALVIK_HEAPSIZE} growthlimit={HW.DALVIK_GROWTHLIMIT} — PRESERVED") | |
| heap_ops = [ | |
| # These OEM values are CORRECT — do not reduce | |
| ("dalvik.vm.heapsize", HW.DALVIK_HEAPSIZE, False), # 512m | |
| ("dalvik.vm.heapgrowthlimit", HW.DALVIK_GROWTHLIMIT, False), # 192m | |
| ("dalvik.vm.heapstartsize", HW.DALVIK_STARTSIZE, False), # 16m | |
| # FIXES | |
| ("dalvik.vm.heapminfree", HW.DALVIK_HEAPMINFREE, True), # 512k→2m | |
| ("dalvik.vm.heapmaxfree", HW.DALVIK_HEAPMAXFREE, True), # 8m→16m | |
| ("dalvik.vm.heaptargetutilization", HW.DALVIK_TARGET_UTIL, False), | |
| # Runtime | |
| ("dalvik.vm.usejit", "true", False), | |
| ("dalvik.vm.usejitprofiles", "true", False), | |
| ("dalvik.vm.dex2oat-filter", "speed-profile", False), | |
| ("dalvik.vm.gctype", "CMS", False), # concurrent GC | |
| ("persist.sys.dalvik.vm.lib.2", "libart.so", False), | |
| ] | |
| for k,v,is_fix in heap_ops: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v) | |
| if is_fix: | |
| L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| else: | |
| L.ok(f"{k} = {v} ✓") | |
| # WebView VM: reduce for TV STB (no browser, 100MB → 50MB saves for SmartTube) | |
| wv_cur = ADB.prop("persist.sys.webview.vmsize") | |
| L.info(f" WebView vmsize current: {int(wv_cur)//1048576 if wv_cur.isdigit() else wv_cur}MB") | |
| ADB.setprop("persist.sys.webview.vmsize","52428800") | |
| L.fix(f" webview.vmsize: {wv_cur} → 52428800 (50MB, TV STB no browser)") | |
| L.ok(f"Dalvik heap: GC minfree 512k→2m + maxfree 8m→16m ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 3 — LMK (PSI-only, minfree /sys DISABLED on this device) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class LMKOptimizer: | |
| """ | |
| CRITICAL: ro.lmk.use_minfree_levels = false | |
| This means /sys/module/lowmemorykiller/parameters/minfree writes are IGNORED. | |
| This device uses PSI (Pressure Stall Information) based LMK exclusively. | |
| PSI-only LMK tuning parameters: | |
| - ro.lmk.upgrade_pressure: 100 → 50 (promote cached processes sooner) | |
| - ro.lmk.downgrade_pressure: 100 → 80 (less aggressive downgrade) | |
| - sys.sysctl.extra_free_kbytes: adjust zone watermark | |
| - OOM score adjustments via /proc/<pid>/oom_score_adj | |
| Confirmed PSI-based LMK state from getprop: | |
| - ro.lmk.use_psi: confirmed via ro.lmk.use_minfree_levels=false | |
| - ro.lmk.low=1001 | medium=800 | critical=0 | |
| - ro.lmk.debug=true (logging enabled) | |
| """ | |
| def apply(self) -> None: | |
| L.hdr("🧹 LMK — PSI-Only Profile (minfree /sys DISABLED on this device)") | |
| L.warn("ro.lmk.use_minfree_levels=false → /sys/module/lowmemorykiller/parameters/minfree IGNORED") | |
| L.info("Using PSI-based thresholds only.") | |
| # PSI LMK props | |
| lmk_props = [ | |
| ("ro.lmk.critical", "0"), # kill only at true critical (confirmed) | |
| ("ro.lmk.kill_heaviest_task", "true"), # confirmed correct | |
| ("ro.lmk.downgrade_pressure", "80"), # relaxed from 100 (less aggressive) | |
| ("ro.lmk.upgrade_pressure", str(HW.LMK_UPGRADE_PRESSURE)), # 100 → 50 FIX | |
| ("ro.lmk.use_minfree_levels", "false"), # confirm — do not change | |
| ("ro.lmk.use_psi", "true"), # explicit PSI enable | |
| ("ro.lmk.filecache_min_kb", "51200"), # 50MB file cache floor | |
| ] | |
| for k,v in lmk_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| # extra_free_kbytes: zone watermark | |
| # Current: 24300 (~23.7MB). Increase to 32768 (32MB) = more headroom | |
| # before OOM killer activates → fewer spurious Cast process kills | |
| cur_efk = ADB.sh("getprop sys.sysctl.extra_free_kbytes",silent=True) | |
| ADB.setprop("sys.sysctl.extra_free_kbytes","32768") | |
| L.fix(f"extra_free_kbytes: {cur_efk} → 32768 (32MB zone watermark)") | |
| ADB.sput("global","background_process_limit","3") | |
| L.ok(" background_process_limit = 3 (SmartTube + Cast + Launcher)") | |
| # OOM score adjustments | |
| L.sub("OOM score — Cast process hardening") | |
| self._harden_oom() | |
| L.ok("PSI LMK profile applied: upgrade_pressure=50, watermark=32MB ✓") | |
| def _harden_oom(self) -> None: | |
| protected_procs = [ | |
| HW.PKG_MEDIASHELL, | |
| "com.google.android.gms", | |
| "com.google.android.nearby", | |
| ] | |
| for pkg in protected_procs: | |
| pid = ADB.sh(f"pidof {pkg}",silent=True).strip() | |
| if pid and pid.isdigit(): | |
| ADB.root(f"echo 100 > /proc/{pid}/oom_score_adj") | |
| L.cast(f"OOM adj=100: {pkg} (PID {pid})") | |
| else: | |
| L.info(f" {pkg.split('.')[-2]} not running — protected at next start") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 4 — NETWORK (kernel 4.9.190, no BBR) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class NetworkOptimizer: | |
| """ | |
| Kernel 4.9.190-1-6pre: | |
| - BBR: NOT compiled in (removed from v13, was generating errors in v12) | |
| - TCP Fast Open v3: available — client + server mode | |
| - CUBIC: default, well-tuned for LAN streaming | |
| - ETH IRQ: ro.nx.eth.irq_mode_mask=3:2 (IRQ coalescing mode 3 on port 2) | |
| DNS dual-path (CRITICAL FIX from v12): | |
| Path 1: setprop net.dns1/net.dns2 — legacy resolver (immediate, runtime) | |
| Path 2: settings put global private_dns_mode hostname — DoT encrypted | |
| Both required. DoT host: 'one.one.one.one' NOT 'dns.cloudflare.com' | |
| mDNS (.local/Cast port 5353 multicast) is UNAFFECTED by either path. | |
| """ | |
| def apply_tcp(self) -> None: | |
| L.hdr("🌐 NETWORK — TCP/IP (Kernel 4.9.190, no BBR)") | |
| L.cast("mDNS (Cast discovery, port 5353) UNAFFECTED by TCP/DNS changes") | |
| # Android-level TCP | |
| ADB.sput("global","net.tcp.buffersize.wifi", | |
| "262144,1048576,2097152,131072,524288,1048576") | |
| L.ok(" WiFi TCP: 256KB/1MB/2MB") | |
| ADB.sput("global","net.tcp.buffersize.ethernet", | |
| "524288,2097152,4194304,262144,1048576,2097152") | |
| L.ok(" Ethernet TCP: 512KB/2MB/4MB") | |
| # FIXED: net.tcp.default_init_rwnd = 60 → 120 | |
| cur = ADB.prop("net.tcp.default_init_rwnd") | |
| ADB.sput("global","tcp_default_init_rwnd","120") | |
| ADB.setprop("net.tcp.default_init_rwnd","120") | |
| L.fix(f" tcp init rwnd: {cur} → 120") | |
| # Kernel TCP params (4.9 supports all of these) | |
| kernel_tcp = [ | |
| ("/proc/sys/net/ipv4/tcp_window_scaling", "1"), | |
| ("/proc/sys/net/ipv4/tcp_timestamps", "1"), | |
| ("/proc/sys/net/ipv4/tcp_sack", "1"), | |
| # Fast Open: 3 = client+server mode (supported in 4.9+) | |
| ("/proc/sys/net/ipv4/tcp_fastopen", "3"), | |
| ("/proc/sys/net/ipv4/tcp_keepalive_intvl", "30"), | |
| ("/proc/sys/net/ipv4/tcp_keepalive_probes", "3"), | |
| ("/proc/sys/net/ipv4/tcp_no_metrics_save", "1"), | |
| # CUBIC is optimal for 4.9 without BBR — tune FQ | |
| ("/proc/sys/net/ipv4/tcp_congestion_control","cubic"), | |
| ] | |
| for path,val in kernel_tcp: | |
| ok = ADB.sysw(path,val) | |
| L.ok(f" ✓ {path.split('/')[-1]} = {val}") if ok else \ | |
| L.warn(f" ⚠ {path.split('/')[-1]} = {val} (sysctl not writable)") | |
| # Net core buffers 16MB | |
| for p in ("/proc/sys/net/core/rmem_max","/proc/sys/net/core/wmem_max"): | |
| ADB.sysw(p,"16777216") | |
| L.ok(" net/core rmem/wmem_max = 16MB") | |
| # WiFi stability | |
| ADB.setprop("wifi.supplicant_scan_interval","300") | |
| ADB.sput("global","wifi_sleep_policy","2") | |
| ADB.sput("global","wifi_power_save","0") | |
| ADB.setprop("persist.debug.wfd.enable","1") | |
| L.ok(" WiFi: scan=300s, sleep_policy=2, power_save=0, WFD=1") | |
| # mDNS Cast reliability | |
| ADB.setprop("ro.mdns.enable_passive_mode","false") | |
| ADB.setprop("net.ssdp.ttl","4") | |
| L.ok(" mDNS: active response mode, SSDP TTL=4") | |
| L.ok("TCP stack: TCP_FO v3 + CUBIC + 16MB bufs + init_rwnd=120 ✓") | |
| def set_dns(self, provider:str="cloudflare") -> None: | |
| info = HW.DNS.get(provider.lower()) | |
| if not info: | |
| L.err(f"Unknown DNS provider: {provider}") | |
| L.info(f" Available: {', '.join(HW.DNS)}") | |
| return | |
| dot,ip1,ip2 = info | |
| L.hdr(f"🔒 DNS — {provider.upper()} ({dot})") | |
| L.cast("mDNS (Chromecast discovery) is UNAFFECTED — unicast DNS only") | |
| # Path 1: legacy resolver (immediate, no reboot) | |
| for k,v in [("net.dns1",ip1),("net.dns2",ip2), | |
| ("net.rmnet0.dns1",ip1),("net.rmnet0.dns2",ip2)]: | |
| ADB.setprop(k,v) | |
| L.ok(f" Legacy DNS: {ip1} / {ip2}") | |
| # Path 2: Private DNS over TLS (persists reboots) | |
| # CORRECTED: 'dns.cloudflare.com' was v10/v11 bug | |
| # Correct hostname: 'one.one.one.one' (resolves to 1.1.1.1) | |
| ADB.sput("global","private_dns_mode","hostname") | |
| ADB.sput("global","private_dns_specifier",dot) | |
| L.ok(f" Private DNS (DoT): {dot}") | |
| # Flush unicast DNS cache | |
| ADB.sh("ndc resolver flushnet 100",silent=True) | |
| ADB.sh("ndc resolver clearnetdns 100",silent=True) | |
| L.ok(" DNS cache flushed") | |
| # Test | |
| ping = ADB.sh(f"ping -c 2 -W 3 {ip1}",silent=True) | |
| if "2 received" in ping: | |
| L.ok(f" Connectivity: {ip1} reachable ✓") | |
| else: | |
| L.warn(f" Ping inconclusive — DoT may still function") | |
| def dns_menu(self) -> None: | |
| L.hdr("🔒 DNS PROVIDER SELECTION") | |
| providers = list(HW.DNS.keys()) | |
| for i,name in enumerate(providers,1): | |
| dot,ip1,ip2 = HW.DNS[name] | |
| L.info(f" {i}. {name.upper():12} DoT: {dot:30} IPs: {ip1}/{ip2}") | |
| L.info(" 0. Keep current") | |
| c = L.C | |
| ch = input(f"\n{c['c']}Select [0-{len(providers)}] > {c['r']}").strip() | |
| if ch=="0": return | |
| try: | |
| idx = int(ch)-1 | |
| if 0<=idx<len(providers): self.set_dns(providers[idx]) | |
| else: L.warn("Invalid") | |
| except ValueError: L.warn("Invalid") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 5 — HDMI + CEC + AUDIO (BCM Nexus-verified) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class HDMIAudio: | |
| """ | |
| All props verified against real getprop output. | |
| Fixed: | |
| - persist.sys.hdmi.keep_awake = false → true (was wrong on device) | |
| Confirmed correct (keep): | |
| - persist.sys.hdmi.addr.playback = 11 (BCM Nexus playback device addr) | |
| - persist.sys.cec.status = true | |
| - persist.nx.hdmi.tx_standby_cec = 1 | |
| - persist.nx.hdmi.tx_view_on_cec = 1 | |
| - persist.nx.vidout.50hz = 0 (locale=pl-PL, 50Hz disabled — see note below) | |
| PAL 50Hz note: locale=pl-PL, timezone=Europe/Amsterdam. | |
| Polish DVB-T content is 25fps. Orange PLAY IPTV uses adaptive rate. | |
| persist.nx.vidout.50hz=0 is correct for HDMI 2.0a sink auto-rate switching. | |
| Only enable if experiencing 25/50fps PAL content stutter. | |
| Audio offload: disabled (BCM7362 HDMI ARC desync root cause confirmed). | |
| vendor.audio-hal-2-0 running — deep buffer path active. | |
| audio.brcm.hdmi.clock_lock=true — locks audio clock to HDMI sink. | |
| """ | |
| def apply_hdmi(self) -> None: | |
| L.hdr("📺 HDMI + CEC — BCM Nexus (addr=11, CEC v1.4 confirmed)") | |
| hdmi_props = [ | |
| # Device type 4 = playback device (confirmed ro.hdmi.device_type=4) | |
| ("ro.hdmi.device_type", "4"), | |
| # addr.playback=11 confirmed correct in getprop | |
| ("persist.sys.hdmi.addr.playback", "11"), | |
| # CEC (all confirmed in getprop) | |
| ("persist.sys.cec.status", "true"), | |
| ("persist.sys.hdmi.tx_standby_cec", "1"), | |
| ("persist.sys.hdmi.tx_view_on_cec", "1"), | |
| ("persist.sys.hdmi.cec_enabled", "1"), | |
| # BCM Nexus CEC (confirmed in getprop) | |
| ("persist.nx.hdmi.tx_standby_cec", "1"), | |
| ("persist.nx.hdmi.tx_view_on_cec", "1"), | |
| # FIXED: was false on device! | |
| ("persist.sys.hdmi.keep_awake", "true"), | |
| # HDR10 | |
| ("persist.sys.hdr.enable", "1"), | |
| # No HDMI hotplug reset | |
| ("ro.hdmi.wake_on_hotplug", "false"), | |
| ("persist.sys.media.avsync", "true"), | |
| ] | |
| for k,v in hdmi_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v} ✓") | |
| # 50Hz — PAL region check | |
| hz50 = ADB.prop("persist.nx.vidout.50hz") | |
| L.info(f" 50Hz mode: {hz50} (pl-PL locale, HDMI auto-rate switching = correct)") | |
| # CEC settings namespace | |
| ADB.sput("global","hdmi_cec_enabled","1") | |
| L.ok(" hdmi_cec_enabled = 1") | |
| L.ok("HDMI: keep_awake=TRUE + CEC v1.4 + BCM Nexus addr=11 ✓") | |
| def apply_audio(self) -> None: | |
| L.hdr("🔊 AUDIO — A/V Sync (BCM7362 HDMI ARC desync fix)") | |
| L.info(" Root cause: audio offload path uses BCM proprietary timing") | |
| L.info(" that disagrees with HDMI ARC → drift 50-200ms over time.") | |
| L.info(" vendor.audio-hal-2-0 RUNNING (confirmed in init.svc)") | |
| audio_props = [ | |
| # Disable offload (desync root cause) | |
| ("audio.offload.disable", "1"), | |
| ("audio.offload.video", "0"), | |
| ("tunnel.audio.encode", "false"), | |
| # Deep buffer: stable 20ms latency baseline | |
| ("audio.deep_buffer.media", "true"), | |
| ("af.fast_track_multiplier", "1"), | |
| # BCM HDMI clock lock (eliminates slow drift) | |
| ("audio.brcm.hdmi.clock_lock", "true"), | |
| ("audio.brcm.hal.latency", "20"), | |
| ] | |
| for k,v in audio_props: | |
| cur = ADB.prop(k) | |
| if cur != v: | |
| ADB.setprop(k,v); L.fix(f"{k}: {cur} → {v}") | |
| else: | |
| L.ok(f"{k} = {v}") | |
| L.ok("Audio: offload disabled + HDMI clock locked ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 6 — SYSTEM RESPONSIVENESS (I/O + CPU + animations) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class Responsiveness: | |
| def apply(self, anim:float=0.5) -> None: | |
| L.hdr(f"🎨 RESPONSIVENESS — I/O + A15 CPU + Animations") | |
| # Animations (0.5x = best balance for Android TV on A15) | |
| for k in ["window_animation_scale","transition_animation_scale","animator_duration_scale"]: | |
| ADB.sput("global",k,str(anim)); L.ok(f" {k} = {anim}x") | |
| # TV recommendations off (saves CPU polling + ~40MB RAM) | |
| ADB.sh("settings put secure tv_disable_recommendations 1",silent=True) | |
| ADB.sh("settings put secure tv_enable_preview_programs 0",silent=True) | |
| ADB.sh("settings put secure tv_watch_next_enabled 0",silent=True) | |
| L.ok(" TV recommendations: disabled") | |
| # Logging reduction | |
| ADB.setprop("persist.logd.size","32768") | |
| ADB.setprop("log.tag.stats_log","OFF") | |
| ADB.setprop("log.tag.statsd","OFF") | |
| L.ok(" Log buffer: 32KB, stats logging OFF") | |
| # I/O scheduler: deadline for eMMC (low-latency VP9 segment reads) | |
| ADB.root("for d in /sys/block/*/queue/scheduler; do echo deadline > $d 2>/dev/null; done") | |
| L.ok(" I/O scheduler: deadline (all block devices)") | |
| # Read-ahead: 512KB (VP9 segment prefetch, fits VP9 tile stream) | |
| ADB.root("for d in /sys/block/*/queue/read_ahead_kb; do echo 512 > $d 2>/dev/null; done") | |
| L.ok(" read_ahead_kb: 512") | |
| # CPU governor: performance on both A15 cores | |
| for cpu in range(2): | |
| path = f"/sys/devices/system/cpu/cpu{cpu}/cpufreq/scaling_governor" | |
| ADB.root(f"echo performance > {path}") | |
| L.ok(f" cpu{cpu}: performance governor (A15 @ full ~1.0GHz)") | |
| # Profiler off | |
| ADB.setprop("persist.sys.profiler_ms","0") | |
| ADB.setprop("persist.sys.strictmode.visual","") | |
| L.ok("Responsiveness: deadline I/O + A15 performance governor + 0.5x anim ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 7 — SAFE DEBLOAT (Cast gate enforced) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| DEBLOAT_DB: List[Tuple[str,str]] = [ | |
| # Confirmed safe based on init.svc.* from getprop (none of these appear) | |
| ("com.google.android.backdrop", "Ambient screensaver — idle GPU + ~30MB"), | |
| ("com.google.android.tvrecommendations", "Recommendations — HTTP polling"), | |
| ("com.google.android.katniss", "Voice overlay — high idle CPU on A15"), | |
| ("com.google.android.tungsten.setupwraith","Setup wizard — done"), | |
| ("com.google.android.marvin.talkback", "TTS accessibility — 40MB unused"), | |
| ("com.google.android.onetimeinitializer","One-time init — completed"), | |
| ("com.google.android.feedback", "Feedback service — periodic ping"), | |
| ("com.google.android.speech.pumpkin", "Hotword detection — CPU drain"), | |
| ("com.android.printspooler", "Print service — no printers on TV"), | |
| ("com.android.dreams.basic", "Basic screensaver"), | |
| ("com.android.dreams.phototable", "Photo screensaver"), | |
| ("com.android.providers.calendar", "Calendar — unused on TV"), | |
| ("com.android.providers.contacts", "Contacts — unused on TV"), | |
| ("com.sagemcom.stb.setupwizard", "Sagemcom factory setup — done"), | |
| ("com.google.android.play.games", "Play Games — unused on TV"), | |
| ("com.google.android.videos", "Play Movies — unused on TV"), | |
| ("com.amazon.amazonvideo.livingroom", "Amazon Prime — use standalone APK"), | |
| ] | |
| class SafeDebloat: | |
| def run(self) -> None: | |
| L.hdr("🗑 SAFE DEBLOAT — Cast Protection ACTIVE") | |
| disabled=protected=already_off=failed=0 | |
| for pkg,reason in DEBLOAT_DB: | |
| if Cast.is_protected(pkg): | |
| protected+=1 | |
| L.cast(f"PROTECTED: {pkg}") | |
| L.dim(Cast.reason(pkg)) | |
| continue | |
| if not ADB.pkg_ok(pkg): | |
| already_off+=1; continue | |
| r = ADB.sh(f"pm disable-user --user 0 {pkg}",silent=True) | |
| if "disabled" in r.lower() or not r: | |
| disabled+=1; L.ok(f"Disabled: {pkg}") | |
| L.dim(reason) | |
| else: | |
| failed+=1; L.warn(f"Could not disable: {pkg}") | |
| L.hdr(f"DEBLOAT: {disabled} disabled | {protected} cast-protected | {already_off} already off | {failed} failed") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 8 — CHROMECAST SERVICE MANAGER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class CastManager: | |
| """ | |
| mdnsd: confirmed RUNNING (init.svc.mdnsd=running from getprop). | |
| mediashell: was in device's debloat.sh kill-list — WRONG. Protected here. | |
| """ | |
| @staticmethod | |
| def audit() -> Dict[str,bool]: | |
| L.hdr("🔍 CHROMECAST AUDIT") | |
| L.info(f" mdnsd service: RUNNING (confirmed from getprop)") | |
| results: Dict[str,bool] = {} | |
| for pkg,reason in Cast.PROTECTED.items(): | |
| ok = ADB.pkg_ok(pkg) | |
| results[pkg] = ok | |
| (L.ok if ok else L.err)(f" {'✓' if ok else '✗'} {pkg}") | |
| L.dim(reason) | |
| broken = [p for p,e in results.items() if not e] | |
| if broken: | |
| L.warn(f"{len(broken)} Cast service(s) DISABLED — use option 7 to restore") | |
| else: | |
| L.ok("All Chromecast services healthy ✓") | |
| return results | |
| @staticmethod | |
| def restore() -> None: | |
| L.hdr("🛡 CHROMECAST RESTORATION") | |
| for pkg in Cast.PROTECTED: | |
| ADB.sh(f"pm enable {pkg}",silent=True) | |
| ADB.sh(f"pm enable --user 0 {pkg}",silent=True) | |
| L.cast(f"Ensured: {pkg}") | |
| L.ok("All Cast services re-enabled ✓") | |
| @staticmethod | |
| def network() -> None: | |
| L.sub("Cast mDNS network tuning") | |
| ADB.sput("global","wifi_sleep_policy","2") | |
| ADB.sput("global","wifi_power_save","0") | |
| ADB.setprop("ro.mdns.enable_passive_mode","false") | |
| ADB.setprop("net.ssdp.ttl","4") | |
| L.ok("Cast mDNS: active response + WiFi always-on ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MODULE 9 — AOT COMPILER | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class AOT: | |
| """ | |
| Confirmed packages from real ps output: | |
| - org.smarttube.stable (u0_a89, PID 6624) | |
| - com.spocky.projengmenu Projectivy (u0_a88, PID 26563) | |
| - com.google.android.apps.mediashell (cast daemon) | |
| - com.google.android.gms.persistent (u0_a12, PID 26127) | |
| dex2oat-Xmx=512m confirmed — speed-profile AOT uses full budget. | |
| """ | |
| APPS: Dict[str,str] = { | |
| HW.PKG_SMARTTUBE_STABLE: "SmartTube Stable", | |
| HW.PKG_PROJECTIVY: "Projectivy Launcher", | |
| HW.PKG_MEDIASHELL: "Cast Daemon (mediashell)", | |
| "com.google.android.gms": "GMS (Cast SDK)", | |
| } | |
| @classmethod | |
| def compile_all(cls) -> None: | |
| L.hdr("⚡ AOT COMPILATION — Eliminate JIT bursts on A15 dual-core") | |
| L.info(f" dex2oat budget: -Xmx {HW.DEX2OAT_XMX} (confirmed)") | |
| for pkg,name in cls.APPS.items(): | |
| if not ADB.pkg_exists(pkg): | |
| L.dim(f"{name}: not installed — skip"); continue | |
| L.info(f" Compiling {name} (speed-profile)... ~60-90s") | |
| r = ADB.sh(f"cmd package compile -m speed-profile -f {pkg}",silent=True) | |
| if "success" in r.lower(): | |
| L.ok(f" {name}: compiled (speed-profile)") | |
| else: | |
| ADB.sh(f"cmd package compile -m speed -f {pkg}",silent=True) | |
| L.ok(f" {name}: compiled (speed fallback)") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # DIAGNOSTIC ENGINE (precision — hardware-aware) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| @dataclass | |
| class DResult: | |
| cat:str; check:str; status:Status | |
| found:str; expected:str=""; fix_fn=None; detail:str="" | |
| @property | |
| def bad(self)->bool: return self.status in (Status.BROKEN,Status.MISSING) | |
| class Diag: | |
| """ | |
| 8-category interactive self-diagnostics. | |
| Each check is hardware-grounded (values from real getprop). | |
| """ | |
| def __init__(self): | |
| self.results: List[DResult] = [] | |
| def _r(self,cat,check,status,found,expected="",fix_fn=None,detail="") -> DResult: | |
| d=DResult(cat,check,status,found,expected,fix_fn,detail) | |
| self.results.append(d); return d | |
| # ── A: System Health ──────────────────────────────────────────────────── | |
| def check_system(self) -> List[DResult]: | |
| res=[]; cat="SYS" | |
| mem = ADB.sh("cat /proc/meminfo",silent=True) | |
| fields={l.split()[0].rstrip(":"):int(l.split()[1]) | |
| for l in mem.splitlines() if len(l.split())>=2 and l.split()[1].isdigit()} | |
| avail_mb = fields.get("MemAvailable",0)//1024 | |
| total_mb = fields.get("MemTotal",0)//1024 | |
| pct = avail_mb/total_mb*100 if total_mb else 0 | |
| s = Status.OK if pct>30 else (Status.WARN if pct>15 else Status.BROKEN) | |
| res.append(self._r(cat,"RAM Available",s,f"{avail_mb}MB ({pct:.0f}%)",">30% OK", | |
| None,f"Total:{total_mb}MB | Nexus:{HW.NX_HEAP_TOTAL}MB reserved")) | |
| # Kernel version | |
| kver = ADB.sh("uname -r",silent=True) | |
| res.append(self._r(cat,"Kernel",Status.OK,kver,HW.KERNEL_VER)) | |
| # CPU variant | |
| variant = ADB.prop("dalvik.vm.isa.arm.variant") | |
| res.append(self._r(cat,"CPU ISA variant",Status.OK if variant==HW.ISA_VARIANT else Status.WARN, | |
| variant,HW.ISA_VARIANT)) | |
| # Thermal | |
| for z in range(2): | |
| raw = ADB.sh(f"cat /sys/class/thermal/thermal_zone{z}/temp",silent=True) | |
| if raw and raw.lstrip("-").isdigit(): | |
| temp = int(raw)/1000 | |
| s = Status.OK if temp<60 else (Status.WARN if temp<75 else Status.BROKEN) | |
| res.append(self._r(cat,f"Thermal zone{z}",s,f"{temp:.1f}°C","<60°C")) | |
| # Storage | |
| df = ADB.sh("df -h /data",silent=True).splitlines() | |
| if len(df)>1: | |
| parts=df[1].split() | |
| pct_str=parts[4] if len(parts)>4 else "?" | |
| use=int(pct_str.replace("%","")) if pct_str!="?" else 0 | |
| s=Status.OK if use<80 else (Status.WARN if use<90 else Status.BROKEN) | |
| res.append(self._r(cat,"/data storage",s,pct_str,"<80%")) | |
| # Internet | |
| ping=ADB.sh("ping -c 2 -W 3 1.1.1.1",silent=True) | |
| res.append(self._r(cat,"Internet", | |
| Status.OK if "2 received" in ping else Status.BROKEN, | |
| "OK" if "2 received" in ping else "OFFLINE")) | |
| # mdnsd (critical for Cast discovery) | |
| mdns=ADB.sh("getprop init.svc.mdnsd",silent=True) | |
| res.append(self._r(cat,"mdnsd (Cast discovery)", | |
| Status.OK if mdns=="running" else Status.BROKEN, | |
| mdns,"running")) | |
| return res | |
| # ── B: Cast Services ──────────────────────────────────────────────────── | |
| def check_cast(self) -> List[DResult]: | |
| res=[]; cat="CAST" | |
| for pkg,reason in Cast.PROTECTED.items(): | |
| ok=ADB.pkg_ok(pkg) | |
| res.append(self._r(cat,pkg.split(".")[-1], | |
| Status.OK if ok else Status.BROKEN, | |
| "enabled" if ok else "DISABLED","enabled", | |
| CastManager.restore,reason)) | |
| return res | |
| # ── C: SmartTube ──────────────────────────────────────────────────────── | |
| def check_smarttube(self) -> List[DResult]: | |
| res=[]; cat="STUBE" | |
| found_pkg=next((p for p in [HW.PKG_SMARTTUBE_STABLE,HW.PKG_SMARTTUBE_BETA,HW.PKG_SMARTTUBE_LEGACY] | |
| if ADB.pkg_exists(p)),None) | |
| if found_pkg: | |
| ver=ADB.pkg_ver(found_pkg) | |
| res.append(self._r(cat,"Installed",Status.OK,f"{found_pkg} v{ver}")) | |
| # Old package migration check | |
| if found_pkg==HW.PKG_SMARTTUBE_LEGACY: | |
| res.append(self._r(cat,"Package name",Status.WARN, | |
| "Legacy package (com.liskovsoft.*)", | |
| "org.smarttube.stable",None, | |
| "New SmartTube uses org.smarttube.stable")) | |
| else: | |
| res.append(self._r(cat,"Installed",Status.MISSING,"NOT INSTALLED", | |
| HW.PKG_SMARTTUBE_STABLE, | |
| lambda: APK.fetch_install(HW.URL_SMARTTUBE_STABLE, | |
| HW.PKG_SMARTTUBE_STABLE,"SmartTube Stable"))) | |
| # Codec props | |
| ve=VideoEngine() | |
| for prop,exp in [("media.vcodec.preferhw","true"), | |
| ("debug.stagefright.ccodec","1"), | |
| ("media.tunneled-playback.enable","true"), | |
| ("media.codec.av1.disable","true"), | |
| ("media.brcm.mma.enable","1"), | |
| ("dalvik.vm.isa.arm.features",HW.ISA_FEATURES_OPT)]: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ve.codec_pipeline)) | |
| return res | |
| # ── D: Video Pipeline ─────────────────────────────────────────────────── | |
| def check_video(self) -> List[DResult]: | |
| res=[]; cat="VIDEO"; ve=VideoEngine() | |
| checks=[ | |
| ("debug.hwui.renderer", "skiagl"), | |
| ("debug.renderengine.backend", "skiaglthreaded"), | |
| ("debug.sf.hw", "1"), | |
| ("debug.gr.numframebuffers", "3"), | |
| ("debug.hwui.layer_cache_size", "32768"), # updated for V3D | |
| ("persist.sys.ui.hw", "true"), # was false! | |
| ("debug.sf.latch_unsignaled", "1"), | |
| ("debug.sf.disable_backpressure", "1"), | |
| ("media.stagefright.cache-params", "65536/131072/30"), # was wrong | |
| ("media.brcm.vpu.buffers", str(HW.VDEC_OUTPORT_BUFFERS)), | |
| ] | |
| for prop,exp in checks: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ve.rendering)) | |
| return res | |
| # ── E: Network + DNS ──────────────────────────────────────────────────── | |
| def check_network(self) -> List[DResult]: | |
| res=[]; cat="NET"; no=NetworkOptimizer() | |
| dot_host=ADB.sget("global","private_dns_specifier") | |
| dot_mode=ADB.sget("global","private_dns_mode") | |
| ip1=ADB.prop("net.dns1") | |
| valid_dots=[v[0] for v in HW.DNS.values()] | |
| dns_ok=dot_host in valid_dots and dot_mode=="hostname" | |
| res.append(self._r(cat,"Private DNS (DoT)", | |
| Status.OK if dns_ok else Status.BROKEN, | |
| f"mode={dot_mode}, host={dot_host}", | |
| "hostname + one.one.one.one", | |
| lambda: no.set_dns("cloudflare"), | |
| f"Legacy net.dns1={ip1}")) | |
| # Detect old wrong hostname | |
| if dot_host=="dns.cloudflare.com": | |
| res.append(self._r(cat,"DNS hostname (v10/v11 bug)",Status.BROKEN, | |
| "dns.cloudflare.com (WRONG — will fail DoT handshake)", | |
| "one.one.one.one",lambda: no.set_dns("cloudflare"))) | |
| rwnd=ADB.prop("net.tcp.default_init_rwnd") | |
| res.append(self._r(cat,"TCP init rwnd", | |
| Status.OK if rwnd=="120" else Status.WARN, | |
| rwnd or "not set","120",no.apply_tcp)) | |
| tfo=ADB.sh("cat /proc/sys/net/ipv4/tcp_fastopen",silent=True).strip() | |
| res.append(self._r(cat,"TCP Fast Open", | |
| Status.OK if tfo=="3" else Status.WARN, | |
| tfo or "not set","3 (client+server)")) | |
| return res | |
| # ── F: Audio ──────────────────────────────────────────────────────────── | |
| def check_audio(self) -> List[DResult]: | |
| res=[]; cat="AUDIO"; ha=HDMIAudio() | |
| for prop,exp in [("audio.offload.disable","1"), | |
| ("audio.deep_buffer.media","true"), | |
| ("audio.brcm.hdmi.clock_lock","true"), | |
| ("tunnel.audio.encode","false"), | |
| ("persist.sys.hdmi.keep_awake","true")]: # was false! | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ha.apply_audio)) | |
| return res | |
| # ── G: Memory + LMK ───────────────────────────────────────────────────── | |
| def check_memory(self) -> List[DResult]: | |
| res=[]; cat="MEM" | |
| mo=DalvikHeap(); lm=LMKOptimizer() | |
| # Dalvik: check OEM values preserved + fixes applied | |
| for prop,exp,fn in [ | |
| ("dalvik.vm.heapsize", HW.DALVIK_HEAPSIZE, mo.apply), # 512m | |
| ("dalvik.vm.heapgrowthlimit",HW.DALVIK_GROWTHLIMIT, mo.apply), # 192m | |
| ("dalvik.vm.heapminfree", HW.DALVIK_HEAPMINFREE, mo.apply), # 2m | |
| ("dalvik.vm.heapmaxfree", HW.DALVIK_HEAPMAXFREE, mo.apply), # 16m | |
| ("dalvik.vm.usejit", "true", mo.apply), | |
| ("ro.lmk.upgrade_pressure",str(HW.LMK_UPGRADE_PRESSURE),lm.apply), # 50 | |
| ("ro.lmk.kill_heaviest_task","true", lm.apply), | |
| ]: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,fn)) | |
| # PSI LMK confirmation | |
| minfree_lvl=ADB.prop("ro.lmk.use_minfree_levels") | |
| res.append(self._r(cat,"LMK use_minfree_levels", | |
| Status.OK if minfree_lvl=="false" else Status.WARN, | |
| minfree_lvl,"false (PSI-only = correct on this device)")) | |
| return res | |
| # ── H: HDMI + CEC ─────────────────────────────────────────────────────── | |
| def check_hdmi(self) -> List[DResult]: | |
| res=[]; cat="HDMI"; ha=HDMIAudio() | |
| for prop,exp in [ | |
| ("persist.sys.cec.status", "true"), | |
| ("persist.sys.hdmi.addr.playback", "11"), # BCM Nexus confirmed | |
| ("persist.sys.hdmi.keep_awake", "true"), # was false! | |
| ("persist.nx.hdmi.tx_standby_cec", "1"), | |
| ("persist.nx.hdmi.tx_view_on_cec", "1"), | |
| ("persist.sys.hdr.enable", "1"), | |
| ]: | |
| v=ADB.prop(prop) | |
| res.append(self._r(cat,prop.split(".")[-1], | |
| Status.OK if v==exp else Status.BROKEN, | |
| v or "not set",exp,ha.apply_hdmi)) | |
| return res | |
| # ── Run category ──────────────────────────────────────────────────────── | |
| def run_cat(self, cat_id:str) -> List[DResult]: | |
| fns = {"A":("System Health", self.check_system), | |
| "B":("Cast Services", self.check_cast), | |
| "C":("SmartTube", self.check_smarttube), | |
| "D":("Video Pipeline", self.check_video), | |
| "E":("Network/DNS", self.check_network), | |
| "F":("Audio", self.check_audio), | |
| "G":("Memory/LMK", self.check_memory), | |
| "H":("HDMI/CEC", self.check_hdmi)} | |
| entry=fns.get(cat_id.upper()) | |
| if not entry: return [] | |
| name,fn=entry | |
| L.hdr(f"🔎 DIAG [{cat_id}] — {name}") | |
| results=fn() | |
| self._print(results) | |
| return results | |
| def _print(self, results:List[DResult]) -> None: | |
| ok=sum(1 for r in results if r.status==Status.OK) | |
| bad=sum(1 for r in results if r.bad) | |
| for r in results: | |
| if r.status==Status.OK: | |
| L.ok(f"[{r.cat}] {r.check}: {r.found}") | |
| elif r.status==Status.WARN: | |
| L.warn(f"[{r.cat}] {r.check}: {r.found} (expected: {r.expected})") | |
| else: | |
| L.err(f"[{r.cat}] {r.check}: {r.found} (expected: {r.expected})") | |
| if r.detail: L.dim(r.detail) | |
| L.info(f"\n Results: {ok} OK | {bad} NEED REPAIR") | |
| def run_all(self) -> None: | |
| L.hdr("🔎 INTERACTIVE DIAGNOSTICS — 8 Hardware-Targeted Categories") | |
| cat_names={ | |
| "A":"System Health","B":"Cast Services","C":"SmartTube", | |
| "D":"Video Pipeline","E":"Network/DNS","F":"Audio", | |
| "G":"Memory/LMK","H":"HDMI/CEC" | |
| } | |
| all_bad: List[DResult] = [] | |
| for cid,cname in cat_names.items(): | |
| L.info(f"\n[{cid}] {cname}") | |
| results=self.run_cat(cid) | |
| bad=[r for r in results if r.bad] | |
| all_bad.extend(bad) | |
| if bad: | |
| c=L.C | |
| ch=input(f" {c['w']}{len(bad)} issue(s). Repair? [Y/n/s=skip all] > {c['r']}").strip().lower() | |
| if ch=="s": break | |
| if ch in ("","y"): self._repair(bad) | |
| else: | |
| L.ok(f" {cname}: ALL OK ✓") | |
| # Summary | |
| L.hdr("📋 DIAGNOSTIC SUMMARY") | |
| total=len(self.results); ok=sum(1 for r in self.results if r.status==Status.OK) | |
| bad=sum(1 for r in self.results if r.bad) | |
| warn=sum(1 for r in self.results if r.status==Status.WARN) | |
| L.ok(f" {ok}/{total} OK"); L.warn(f" {warn} WARN"); L.err(f" {bad} BROKEN") | |
| if all_bad: | |
| L.warn(" Unresolved:") | |
| for r in all_bad: | |
| if r.bad: L.err(f" [{r.cat}] {r.check}: {r.found}") | |
| def _repair(self, bad:List[DResult]) -> None: | |
| seen:set=set() | |
| for r in bad: | |
| if r.fix_fn and id(r.fix_fn) not in seen: | |
| seen.add(id(r.fix_fn)) | |
| L.fix(f"Repairing: [{r.cat}] {r.check}") | |
| try: r.fix_fn() | |
| except Exception as e: L.err(f"Repair error: {e}") | |
| def menu(self) -> None: | |
| c=L.C | |
| cat_map={"A":"System Health","B":"Cast Services","C":"SmartTube", | |
| "D":"Video Pipeline","E":"Network/DNS","F":"Audio", | |
| "G":"Memory/LMK","H":"HDMI/CEC","*":"All (interactive)"} | |
| L.hdr("🔎 DIAGNOSTICS — Select Category") | |
| for k,v in cat_map.items(): | |
| L.info(f" {c['c']}{k}{c['r']}. {v}") | |
| ch=input(f"\n{c['c']}Category [A-H or *] > {c['r']}").strip().upper() | |
| if ch=="*": | |
| self.run_all() | |
| elif ch in cat_map: | |
| results=self.run_cat(ch) | |
| bad=[r for r in results if r.bad] | |
| if bad: | |
| fix=input(f"\n{c['w']}Auto-repair {len(bad)} issue(s)? [Y/n] > {c['r']}").strip().lower() | |
| if fix in ("","y"): self._repair(bad) | |
| else: | |
| L.warn("Invalid category") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # AUTO REPAIR ENGINE | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class Repair: | |
| """ | |
| 11 repair sectors — all targeted to real device state. | |
| Detection lambdas use actual getprop values as baseline. | |
| """ | |
| REGISTRY: List[Dict] = [ | |
| {"id":"smarttube_missing","name":"SmartTube not installed", | |
| "detect": lambda: not ADB.pkg_exists(HW.PKG_SMARTTUBE_STABLE), | |
| "repair": lambda: APK.fetch_install(HW.URL_SMARTTUBE_STABLE,HW.PKG_SMARTTUBE_STABLE,"SmartTube Stable")}, | |
| {"id":"smarttube_old_pkg","name":"SmartTube old package (com.teamsmart → org.smarttube)", | |
| "detect": lambda: ADB.pkg_exists("com.teamsmart.videomanager.tv"), | |
| "repair": lambda: APK.fetch_install(HW.URL_SMARTTUBE_STABLE,HW.PKG_SMARTTUBE_STABLE,"SmartTube Stable (migrated)")}, | |
| {"id":"cast_mediashell","name":"Cast daemon (mediashell) DISABLED — device debloat.sh damage", | |
| "detect": lambda: not ADB.pkg_ok(HW.PKG_MEDIASHELL), | |
| "repair": CastManager.restore}, | |
| {"id":"cast_gms","name":"GMS (Cast SDK) disabled", | |
| "detect": lambda: not ADB.pkg_ok("com.google.android.gms"), | |
| "repair": CastManager.restore}, | |
| {"id":"wrong_dns_old","name":"DNS wrong hostname: dns.cloudflare.com (v10/v11 bug)", | |
| "detect": lambda: ADB.sget("global","private_dns_specifier")=="dns.cloudflare.com", | |
| "repair": lambda: NetworkOptimizer().set_dns("cloudflare")}, | |
| {"id":"dns_not_set","name":"Private DNS not configured (mode != hostname)", | |
| "detect": lambda: ADB.sget("global","private_dns_mode")!="hostname", | |
| "repair": lambda: NetworkOptimizer().set_dns("cloudflare")}, | |
| {"id":"ui_hw_false","name":"persist.sys.ui.hw=false (GPU force rendering disabled)", | |
| "detect": lambda: ADB.prop("persist.sys.ui.hw")!="true", | |
| "repair": lambda: ADB.setprop("persist.sys.ui.hw","true")}, | |
| {"id":"hdmi_keep_awake","name":"persist.sys.hdmi.keep_awake=false (HDMI drops during buffering)", | |
| "detect": lambda: ADB.prop("persist.sys.hdmi.keep_awake")!="true", | |
| "repair": lambda: ADB.setprop("persist.sys.hdmi.keep_awake","true")}, | |
| {"id":"av1_active","name":"AV1 SW decoder active (100% CPU on A15 — confirmed no HW)", | |
| "detect": lambda: ADB.prop("media.codec.av1.disable")!="true", | |
| "repair": VideoEngine().suppress_av1}, | |
| {"id":"idiv_disabled","name":"A15 hardware idiv not enabled in Dalvik ISA features", | |
| "detect": lambda: ADB.prop("dalvik.vm.isa.arm.features")!=HW.ISA_FEATURES_OPT, | |
| "repair": lambda: ADB.setprop("dalvik.vm.isa.arm.features",HW.ISA_FEATURES_OPT)}, | |
| {"id":"heap_minfree","name":"dalvik.vm.heapminfree=512k (too small — GC micro-pauses)", | |
| "detect": lambda: ADB.prop("dalvik.vm.heapminfree") not in ("2m",""), | |
| "repair": DalvikHeap().apply}, | |
| {"id":"cache_params","name":"media.stagefright.cache-params too small (32768/65536/25)", | |
| "detect": lambda: ADB.prop("media.stagefright.cache-params")=="32768/65536/25", | |
| "repair": lambda: ADB.setprop("media.stagefright.cache-params","65536/131072/30")}, | |
| {"id":"tcp_rwnd","name":"net.tcp.default_init_rwnd=60 (half optimal)", | |
| "detect": lambda: ADB.prop("net.tcp.default_init_rwnd") not in ("120",""), | |
| "repair": lambda: (ADB.setprop("net.tcp.default_init_rwnd","120"), | |
| ADB.sput("global","tcp_default_init_rwnd","120"))}, | |
| {"id":"lmk_upgrade","name":"ro.lmk.upgrade_pressure=100 (too high — slow cached proc recovery)", | |
| "detect": lambda: ADB.prop("ro.lmk.upgrade_pressure")=="100", | |
| "repair": lambda: ADB.setprop("ro.lmk.upgrade_pressure","50")}, | |
| ] | |
| @classmethod | |
| def scan(cls) -> None: | |
| L.hdr("🔧 AUTO-REPAIR — Hardware-Targeted Sector Scan") | |
| found: List[Dict] = [] | |
| for entry in cls.REGISTRY: | |
| try: detected=entry["detect"]() | |
| except Exception: detected=False | |
| if detected: | |
| found.append(entry) | |
| L.err(f" ✗ BROKEN: {entry['name']}") | |
| else: | |
| L.dim(f"✓ OK: {entry['id']}") | |
| if not found: | |
| L.ok("All sectors healthy — no repairs needed ✓"); return | |
| L.warn(f"\n{len(found)} broken sector(s):") | |
| for i,e in enumerate(found,1): | |
| L.info(f" {i}. {e['name']}") | |
| c=L.C | |
| ch=input(f"\n{c['w']}Repair all {len(found)}? [Y=all / n=select / x=cancel] > {c['r']}").strip().lower() | |
| if ch=="x": return | |
| if ch=="n": | |
| for i,e in enumerate(found,1): | |
| sub=input(f" [{i}] {e['name']}\n Repair? [Y/n] > ").strip().lower() | |
| if sub in ("","y"): cls._do(e) | |
| else: | |
| for e in found: cls._do(e) | |
| L.ok("Auto-repair complete ✓") | |
| @classmethod | |
| def _do(cls,e:Dict)->None: | |
| L.fix(f"Repairing: {e['name']}") | |
| try: e["repair"]() | |
| except Exception as ex: L.err(f"Error: {ex}") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MEMORY DEEP CLEAN | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| def deep_clean() -> None: | |
| L.hdr("🔄 DEEP CLEAN — Cast-Safe") | |
| ADB.sh("am kill-all",silent=True); L.ok(" am kill-all") | |
| ADB.sh("pm trim-caches 2G",silent=True); L.ok(" pm trim-caches 2G") | |
| ADB.sh("dumpsys batterystats --reset",silent=True) | |
| ADB.root("sync && echo 3 > /proc/sys/vm/drop_caches") | |
| L.ok(" drop_caches") | |
| L.cast("Restoring Cast services post-clean...") | |
| CastManager.restore() | |
| L.ok("Deep clean: Cast services verified ✓") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # SHIZUKU | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| def deploy_shizuku() -> None: | |
| L.hdr("🔑 SHIZUKU — Privilege Engine") | |
| if not ADB.pkg_exists(HW.PKG_SHIZUKU): | |
| APK.fetch_install(HW.URL_SHIZUKU,HW.PKG_SHIZUKU,"Shizuku") | |
| else: | |
| L.ok("Shizuku already installed") | |
| cmd=("P=$(pm path moe.shizuku.privileged.api | cut -d: -f2); " | |
| "CLASSPATH=$P app_process /system/bin " | |
| "--nice-name=shizuku_server moe.shizuku.server.ShizukuServiceServer &") | |
| ADB.sh(cmd); time.sleep(3); L.ok("Shizuku server started") | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # MAIN ORCHESTRATOR | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| class App: | |
| def __init__(self, device:str): | |
| self.device=device | |
| self.ve = VideoEngine() | |
| self.dh = DalvikHeap() | |
| self.lmk = LMKOptimizer() | |
| self.net = NetworkOptimizer() | |
| self.ha = HDMIAudio() | |
| self.res = Responsiveness() | |
| self.dbl = SafeDebloat() | |
| self.cast= CastManager() | |
| self.aot = AOT() | |
| self.diag= Diag() | |
| self.rep = Repair() | |
| def _banner(self) -> None: | |
| c=L.C | |
| print(f""" | |
| {c['h']}{c['b']}╔══════════════════════════════════════════════════════════════════════╗ | |
| ║ PLAYBOX TITANIUM v{VERSION} | |
| ║ Sagemcom DCTIW362P │ Android TV 9 │ Kernel 4.9.190 │ ARMv7 A15 | |
| ╠══════════════════════════════════════════════════════════════════════╣ | |
| ║ BCM7362 VPU │ VideoCore GLES3.1 │ MMA=1 │ VDec32 │ V3D Fences | |
| ║ RAM:1459MB │ Nexus:240MB │ Userspace:~{HW.USERSPACE_BUDGET_MB}MB │ PSI-LMK | |
| ╠══════════════════════════════════════════════════════════════════════╣ | |
| ║ 🎬 VP9 HW+Tunnel │ 🛡 Cast Protected │ 🔒 DNS:one.one.one.one | |
| ║ 🔧 A15-idiv │ 🧠 PSI-LMK │ 🔊 HDMI-clock-lock │ ⚡ AOT 512m | |
| ╚══════════════════════════════════════════════════════════════════════╝{c['r']} | |
| Target: {c['c']}{self.device}{c['r']} Build: PTT1.190826.001 | |
| """) | |
| def _menu(self) -> None: | |
| c=L.C | |
| MENU = f""" | |
| {c['s']}VIDEO{c['r']} | |
| {c['s']}1.{c['r']} Codec Pipeline (A15-idiv + MMA + VDec32 + Tunnel) | |
| {c['s']}2.{c['r']} Rendering (skiaglthreaded + V3D explicit fence) | |
| {c['s']}3.{c['r']} AV1 Suppression (no HW on BCM7362 — confirmed) | |
| {c['h']}CHROMECAST{c['r']} | |
| {c['s']}4.{c['r']} 🛡 Audit Cast Services | |
| {c['s']}5.{c['r']} 🛡 Restore Cast Services (emergency) | |
| {c['s']}6.{c['r']} 📡 Cast mDNS Network Tuning | |
| {c['i']}DIAGNOSTICS & REPAIR{c['r']} | |
| {c['i']}D.{c['r']} 🔎 Interactive Diagnostics (8 hw-targeted categories) | |
| {c['i']}R.{c['r']} 🔧 Auto-Repair Engine ({len(Repair.REGISTRY)} hardware sectors) | |
| {c['i']}N.{c['r']} 🔒 DNS Manager (provider selector) | |
| {c['w']}SYSTEM{c['r']} | |
| {c['w']}7. {c['r']} Network TCP (4.9.190 kernel, TCP-FO v3, no BBR) | |
| {c['w']}8. {c['r']} HDMI + CEC (BCM Nexus, addr=11, keep_awake=true) | |
| {c['w']}9. {c['r']} Audio A/V Sync Fix (HDMI clock lock) | |
| {c['w']}10.{c['r']} Dalvik Heap (OEM 512m/192m preserved, minfree fix) | |
| {c['w']}11.{c['r']} LMK (PSI-only — minfree /sys DISABLED on this device) | |
| {c['w']}12.{c['r']} Responsiveness + I/O deadline + A15 performance gov | |
| {c['w']}13.{c['r']} Safe Debloat (Cast Protected) | |
| {c['w']}14.{c['r']} Deep Clean RAM (Cast-Safe) | |
| {c['w']}15.{c['r']} AOT Compile (SmartTube + Cast + GMS, Xmx=512m) | |
| {c['w']}16.{c['r']} Deploy Shizuku | |
| {c['c']}AUTO MODES{c['r']} | |
| {c['c']}20.{c['r']} 🚀 SMARTTUBE ULTRA | |
| {c['c']}21.{c['r']} 🏆 FULL SYSTEM ULTRA | |
| {c['e']}0. {c['r']} Exit""" | |
| while True: | |
| os.system("clear"); self._banner() | |
| print(f"{c['b']}{'═'*68}{c['r']}{MENU}") | |
| print(f"{c['b']}{'═'*68}{c['r']}") | |
| ch=input(f"\n{c['c']}Choice > {c['r']}").strip().lower() | |
| dispatch={ | |
| "1": self.ve.codec_pipeline, | |
| "2": self.ve.rendering, | |
| "3": self.ve.suppress_av1, | |
| "4": self.cast.audit, | |
| "5": self.cast.restore, | |
| "6": self.cast.network, | |
| "d": self.diag.menu, | |
| "r": self.rep.scan, | |
| "n": self.net.dns_menu, | |
| "7": lambda: (self.net.apply_tcp(), self.net.set_dns("cloudflare")), | |
| "8": self.ha.apply_hdmi, | |
| "9": self.ha.apply_audio, | |
| "10":self.dh.apply, | |
| "11":self.lmk.apply, | |
| "12":self.res.apply, | |
| "13":self.dbl.run, | |
| "14":deep_clean, | |
| "15":self.aot.compile_all, | |
| "16":deploy_shizuku, | |
| "20":self.smarttube_ultra, | |
| "21":self.full_ultra, | |
| "0": self._exit, | |
| } | |
| fn=dispatch.get(ch) | |
| if fn: | |
| fn() | |
| else: | |
| L.warn("Invalid choice") | |
| if ch!="0": | |
| input(f"\n{c['c']}Press Enter...{c['r']}") | |
| def _exit(self): | |
| L.save(); sys.exit(0) | |
| # ── SmartTube ULTRA ────────────────────────────────────────────────────── | |
| def smarttube_ultra(self) -> None: | |
| L.hdr("🚀 SMARTTUBE ULTRA — A15+BCM7362 Precision") | |
| steps=[ | |
| ("Auto-Repair pre-check", self.rep.scan), | |
| ("Cast Audit", self.cast.audit), | |
| ("Codec Pipeline (A15+MMA+VDec32)", self.ve.codec_pipeline), | |
| ("Rendering (V3D fence + 32KB cache)",self.ve.rendering), | |
| ("AV1 Suppression", self.ve.suppress_av1), | |
| ("Dalvik Heap (minfree 512k→2m)", self.dh.apply), | |
| ("LMK (PSI-only, upgrade_p=50)", self.lmk.apply), | |
| ("Audio A/V Sync (HDMI clock lock)", self.ha.apply_audio), | |
| ("HDMI + CEC (keep_awake=true)", self.ha.apply_hdmi), | |
| ("Responsiveness + I/O + A15 gov", self.res.apply), | |
| ("TCP + DNS (one.one.one.one)", lambda: (self.net.apply_tcp(), self.net.set_dns())), | |
| ("Cast mDNS tuning", self.cast.network), | |
| ("Cast OOM hardening", self.lmk._harden_oom), | |
| ("AOT Compilation (Xmx=512m)", self.aot.compile_all), | |
| ("Cast Services Final Restore", self.cast.restore), | |
| ] | |
| for i,(name,fn) in enumerate(steps,1): | |
| L.info(f"\n[{i}/{len(steps)}] {name}...") | |
| fn(); time.sleep(0.3) | |
| L.hdr("🎉 SMARTTUBE ULTRA COMPLETE") | |
| L.ok("VP9 HW + Tunnel + A15-idiv + MMA + VDec32 + DNS: one.one.one.one + Cast ✓") | |
| L.warn("SmartTube: Settings → Player → Video codec → VP9") | |
| L.warn("SmartTube: Settings → Player → Use tunnel mode → ON") | |
| L.save() | |
| # ── Full ULTRA ─────────────────────────────────────────────────────────── | |
| def full_ultra(self) -> None: | |
| L.hdr("🏆 FULL SYSTEM ULTRA — All Modules (Hardware-Targeted v13)") | |
| steps=[ | |
| ("System Diagnostics", lambda: self.diag.run_cat("A")), | |
| ("Auto-Repair pre-check", self.rep.scan), | |
| ("Cast Audit", self.cast.audit), | |
| ("Codec Pipeline (A15+MMA+VDec32)", self.ve.codec_pipeline), | |
| ("Rendering (V3D fence)", self.ve.rendering), | |
| ("AV1 Suppression", self.ve.suppress_av1), | |
| ("Dalvik Heap precision fix", self.dh.apply), | |
| ("LMK PSI-only (upgrade_p=50)", self.lmk.apply), | |
| ("Audio A/V Sync", self.ha.apply_audio), | |
| ("HDMI + CEC + BCM Nexus", self.ha.apply_hdmi), | |
| ("TCP + DNS fix (one.one.one.one)", lambda: (self.net.apply_tcp(), self.net.set_dns())), | |
| ("Responsiveness + deadline + A15", self.res.apply), | |
| ("Safe Debloat (Cast Protected)", self.dbl.run), | |
| ("Cast mDNS tuning", self.cast.network), | |
| ("Cast OOM hardening", self.lmk._harden_oom), | |
| ("AOT Compilation", self.aot.compile_all), | |
| ("Deep Clean (Cast-Safe)", deep_clean), | |
| ("Final Cast Audit", self.cast.audit), | |
| ] | |
| for i,(name,fn) in enumerate(steps,1): | |
| L.info(f"\n[{i}/{len(steps)}] {name}...") | |
| fn(); time.sleep(0.2) | |
| L.hdr("🏆 FULL ULTRA COMPLETE") | |
| L.ok("All hardware-targeted optimizations applied. Cast: PROTECTED. DNS: FIXED.") | |
| L.warn(f"Reboot: adb -s {self.device} reboot") | |
| L.save() | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # CLI | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| def parse() -> argparse.Namespace: | |
| p=argparse.ArgumentParser( | |
| description=f"Playbox Titanium v{VERSION} — Sagemcom DCTIW362P", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| epilog=""" | |
| EXAMPLES: | |
| python3 Autopilot_13_PRECISION.py # Interactive menu | |
| python3 Autopilot_13_PRECISION.py --smarttube-ultra # Video ultra | |
| python3 Autopilot_13_PRECISION.py --full-ultra # Full system | |
| python3 Autopilot_13_PRECISION.py --diag # Self-diagnostics | |
| python3 Autopilot_13_PRECISION.py --repair # Auto-repair scan | |
| python3 Autopilot_13_PRECISION.py --cast-restore # Emergency Cast | |
| python3 Autopilot_13_PRECISION.py --dns cloudflare # Fix DNS | |
| python3 Autopilot_13_PRECISION.py --device 192.168.1.3:5555 --full-ultra | |
| """) | |
| p.add_argument("--device", default=None) | |
| p.add_argument("--smarttube-ultra", action="store_true") | |
| p.add_argument("--full-ultra", action="store_true") | |
| p.add_argument("--diag", action="store_true") | |
| p.add_argument("--repair", action="store_true") | |
| p.add_argument("--cast-audit", action="store_true") | |
| p.add_argument("--cast-restore", action="store_true") | |
| p.add_argument("--dns", default=None, metavar="PROVIDER") | |
| p.add_argument("--beta", action="store_true") | |
| return p.parse_args() | |
| def main() -> None: | |
| args=parse() | |
| device=args.device or ADB.detect() or DEFAULT_DEVICE | |
| if not ADB.connect(device): | |
| L.err(f"Cannot connect: {device}"); sys.exit(1) | |
| a=App(device) | |
| if args.cast_restore: CastManager.restore() | |
| elif args.cast_audit: CastManager.audit() | |
| elif args.dns: NetworkOptimizer().set_dns(args.dns) | |
| elif args.diag: a.diag.run_all() | |
| elif args.repair: Repair.scan() | |
| elif args.smarttube_ultra: a.smarttube_ultra() | |
| elif args.full_ultra: a.full_ultra() | |
| else: a._banner(); a._menu() | |
| if __name__=="__main__": | |
| try: | |
| main() | |
| except KeyboardInterrupt: | |
| print(); L.warn("Ctrl+C"); L.save(); sys.exit(0) | |
| except Exception as e: | |
| L.err(f"Fatal: {e}") | |
| import traceback; traceback.print_exc(); sys.exit(1) |
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
| 🔬 Raport Analizy — Samsung Galaxy Watch 4 (SM-R870) | |
| Data sesji: 23.02.2026, godz. 19:26–19:30 | |
| Build: BP2A.250325.020.R870XXU1JYL6 | One UI 8.0 / WearOS 6.0 / Android 16 (SDK 36) | |
| Narzędzie: Watch4 Ultra Suite v3.0.0 | |
| 🔴 KRYTYCZNE — Natychmiastowe działanie wymagane | |
| 1. Partycja /system wypełniona w 100% | |
| /dev/block/dm-0 3.8 GB 3803384 2148 KB wolne 100% /system | |
| To jest najpoważniejszy problem w całym systemie. Zaledwie 2 MB wolnego miejsca na partycji /system oznacza że: | |
| System nie może zapisywać żadnych tymczasowych plików runtime | |
| ART nie może zapisywać skompilowanych profili odex — kompilacja DEX jest blokowana | |
| Aktualizacje OTA są niemożliwe do zastosowania | |
| System może zachowywać się niestabilnie przy intensywnym I/O | |
| Przyczyna: Aktualizacja do One UI 8.0 wypełniła partycję systemową bez odpowiedniego zarządzania miejscem. | |
| Działanie: Uruchomić opcję [3] → [4] Czyść cache + pm trim-caches i sprawdzić po restarcie. Jeśli problem nie ustąpi — konieczny factory reset lub flashowanie stock ROM z odblokowaną partycją. | |
| 2. Katastrofalny jank — WorldClock / UI | |
| Aplikacja: com.samsung.android.watch.worldclock | |
| Klatki łącznie: 23 | |
| Jank: 10 klatek = 43.48% ← KATASTROFA (norma: <5%) | |
| 50. percentyl: 27ms ← powinno być <16ms (cel: 60fps) | |
| 90. percentyl: 89ms ← 5.5x powyżej normy | |
| 99. percentyl: 300ms ← spike widoczny gołym okiem | |
| Number Slow UI thread: 9 ← wątek UI jest wąskim gardłem | |
| Number High input latency: 10 ← każda janky klatka = opóźnienie dotyku | |
| Diagnoza: Wątek UI jest zatrzymywany co kilka klatek na 27–300ms. Przyczyny (w kolejności prawdopodobieństwa): | |
| /system pełny → ART nie kompiluje kodu → brak JIT optimization → interpretacja bajtkodu | |
| AmbientDreamProxy wakelock budzący CPU co ~10s (patrz niżej) | |
| Skia OpenGL zamiast Vulkan — brak hardware-acceleration na W920 | |
| window_animation_scale nadal = 1.0 (nie zmienione, tylko transition i animator = 0.5) | |
| 3. AmbientDreamProxy — ukryty sprawca lagów przy wybudzeniu | |
| Z batterystats (obie sesje z AOD wyłączonym): | |
| 14:02:27 +wake_lock=u0a40:"AmbientDreamProxy" +screen ON reason=TILT | |
| 14:02:30 -screen OFF (3 sekundy po wybudzeniu!) | |
| 14:02:40 +wake_lock=u0a40:"AmbientDreamProxy" +screen ON reason=TILT | |
| 14:02:45 -screen OFF | |
| 14:03:23 +wake_lock=u0a40:"AmbientDreamProxy" wake_reason: contexthub + gnss_mailbox | |
| Kluczowy wniosek: AmbientDreamProxy (Samsung AOD service, UID u0a40) nadal aktywnie przechwytuje zdarzenia TILT i uruchamia wakeable display cycle co ~10 sekund, mimo że doze_always_on=0. Komponent nie jest wyłączony — tylko flaga jest zresetowana. To powoduje: | |
| Cykl CPU wake → AOD init → display power → display off → CPU sleep powtarzający się | |
| Spike prądu przy każdym przebudzeniu = drenaż baterii | |
| Opóźnienie wybudzenia właściwego (zegarek "waha się") | |
| Powiązane: gnss_mailbox (GPS hardware) również budzi urządzenie niezależnie od AOD. | |
| 🟡 WAŻNE — Wysokie ryzyko wydajności | |
| 4. Massive Memory Overcommit | |
| RAM fizyczny: 1.41 GB | |
| Committed_AS: 23.4 GB ← 16.5x więcej niż fizyczny RAM! | |
| SwapTotal (ZRAM): 963 MB (używane: 89 MB / 986 MB) | |
| SUnreclaim: 173 MB ← kernel slab nie do odzyskania | |
| CmaFree: 0 KB ← Contiguous Memory Allocator wyczerpany | |
| System ma zarezerwowane 23x więcej wirtualnego RAM niż posiada fizycznie. Przy szczycie aktywności LMK (Low Memory Killer) agresywnie zabija procesy w tle. | |
| 5. Aktywne dwa procesy Google Play Services | |
| 4471 78.6 MB [gle.android.gms] ← gms unstable | |
| 1256 86.9 MB [.gms.persistent] ← gms persistent | |
| Razem: ~165 MB samego GMS | |
| GMS persistent + GMS unstable działają równolegle, zużywając ~165 MB z 1.4 GB RAM. | |
| 6. Samsung Health — podwójny proces | |
| 3195 60.5 MB [id.wear.shealth] | |
| 6650 56.2 MB [.shealthmonitor] | |
| Razem: ~116 MB | |
| Dwa procesy Samsung Health (aplikacja + monitor) = 116 MB RAM. Na zegarku z 1.4 GB jest to ~8% całkowitego RAM. | |
| 7. Zbędne procesy w tle | |
| 6784 50.3 MB [droid.easyMover] ← Samsung Easy Mover (transfer danych — po co na zegarku?) | |
| 6589 53.5 MB [.samsungaccount] ← Samsung Account service | |
| 6616 42.6 MB [id.app.reminder] ← Samsung Reminder | |
| 6525 41.5 MB [id.diagmonagent] ← Samsung Diagnostic Monitor Agent | |
| Łącznie ~187 MB dla 4 procesów nie związanych z podstawową funkcją zegarka. | |
| 🟢 POZYTYWNE — Co działa poprawnie | |
| Parametr | |
| Wartość | |
| Ocena | |
| Temperatura CPU | |
| 37°C | |
| ✓ Normalna, brak throttlingu | |
| Temperatura GPU | |
| 36°C | |
| ✓ Normalna | |
| Temperatura baterii | |
| 31°C | |
| ✓ Dobra | |
| Throttling termiczny | |
| Status 0 | |
| ✓ Brak | |
| Kondycja baterii | |
| health=2 (Good) | |
| ✓ Dobra | |
| Poziom baterii | |
| 68% | |
| ✓ Wystarczający | |
| Prąd rozładowania | |
| -142 mA | |
| ✓ Niski pobór | |
| ANR | |
| Brak | |
| ✓ | |
| Crash | |
| Brak (ProcessCpuTracker = normalny błąd) | |
| ✓ | |
| OOM | |
| Brak | |
| ✓ | |
| Partycja /data | |
| 14% zajęte | |
| ✓ Dużo miejsca | |
| Partycja /cache | |
| 5% zajęte | |
| ✓ | |
| ⚙️ Znaleziony błąd w skrypcie v3.0.0 | |
| Lokalizacja: Linia 549 — diag_ui_performance() | |
| Błąd: | |
| # STARY KOD (błędny): | |
| local jank_pct=$(( ${janky_frames:-0} * 100 / total_frames )) | |
| # Bash $(( )) nie obsługuje liczb zmiennoprzecinkowych! | |
| # Gdy jank_pct = "43.48%", całe wyrażenie psuje się | |
| Błąd aktywuje się gdy gfxinfo zwraca procent z miejscem dziesiętnym (np. 43.48%). | |
| Fix: Użyć awk zamiast bash arithmetic — patrz skrypt v3.1. | |
| 📋 Plan działania — kolejność według priorytetu | |
| Priorytet | |
| Akcja | |
| Menu w skrypcie | |
| 🔴 1 | |
| Wyczyść cache + trim | |
| [3] → [4] | |
| 🔴 2 | |
| Wyłącz komponenty AOD (AmbientDreamProxy) | |
| [2] → [4] | |
| 🔴 3 | |
| Wyłącz GPS w tle | |
| [2] → [6] (nowe w v3.1) | |
| 🟡 4 | |
| Uruchom Profil TURBO | |
| [T] | |
| 🟡 5 | |
| Debloat: easyMover, diagmonagent, samsungaccount | |
| [4] → [1] | |
| 🟡 6 | |
| Kompilacja ART (bg-dexopt-job) | |
| [3] → [3] | |
| 🟢 7 | |
| Restart po wszystkich zmianach | |
| [5] → [9] | |
| 🔧 Dodatkowe komendy do uruchomienia ręcznie | |
| # Sprawdź czy system ma więcej miejsca po trim | |
| `adb shell df /system` | |
| # Wymuś kill AmbientDreamProxy (tymczasowe) | |
| `adb shell am force-stop com.samsung.android.app.aodservice` | |
| `adb shell am force-stop com.samsung.systemui.aod` | |
| # Wyłącz GNSS wakeup | |
| `adb shell settings put secure location_mode 0` | |
| # Sprawdź BatteryDump — dekodowanie hex fields | |
| # Format: voltage,current,temp_tenth,capacity,level,temp_C/10,... | |
| # Wartość "12600" w polu 16 = full charge capacity (mAh) = 126.00... ? | |
| # (Samsung używa własnego formatu) | |
| # Sprawdź rozmiar /system po restarcie | |
| `adb shell df -h /system` | |
| ༼//FERR༼ට༼/0 ͜༼×༼ Aꈤꪮꈤymousik | |
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
| ## ║ Samsung Galaxy Watch 4 — Ultra Optimizer & Repair Suite ║ | |
| ## ║ One UI 8.0 / WearOS 6.0 / Android 16 · SM-R870 · Exynos W920 ║ | |
| ## Anonymousik.is-a.dev production | |
| #!/usr/bin/env bash | |
| # ╔══════════════════════════════════════════════════════════════════╗ | |
| # ║ Samsung Galaxy Watch 4 — Ultra Optimizer & Repair Suite ║ | |
| # ║ One UI 8.0 / WearOS 6.0 / Android 16 · SM-R870 · Exynos W920 ║ | |
| # ║ Wersja: 3.1.0 | Patch: 2026-02-23 ║ | |
| # ║ Fixes: jank% float bug, /system alert, AmbientDreamProxy, ║ | |
| # ║ GPS/GNSS killer, window_animation_scale, +debloat ║ | |
| # ╚══════════════════════════════════════════════════════════════════╝ | |
| # Bezpieczne, odwracalne, kompleksowe narzędzie do optymalizacji, | |
| # diagnostyki, debloatu i naprawy systemu bez ryzyka brick'a. | |
| # Bash strict mode — wyłączamy set -e bo potrzebujemy obsługi błędów per-komenda | |
| set -uo pipefail | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 0 — ZMIENNE GLOBALNE I STAŁE | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Kolory terminala | |
| RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' | |
| CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' | |
| BLUE='\033[0;34m'; MAGENTA='\033[0;35m'; WHITE='\033[0;37m' | |
| BGREEN='\033[1;32m'; BRED='\033[1;31m'; BYELLOW='\033[1;33m' | |
| BCYAN='\033[1;36m'; DIM='\033[2m' | |
| # Metadane | |
| readonly SCRIPT_NAME="Watch4 Ultra Suite" | |
| readonly VERSION="3.1.0" | |
| readonly SCRIPT_DATE="2026-02-23" | |
| readonly LOG_DIR="/tmp/watch4_suite_logs" | |
| readonly BACKUP_DIR="${LOG_DIR}/backup_$(date +%Y%m%d_%H%M%S)" | |
| readonly LOG_FILE="${LOG_DIR}/session_$(date +%Y%m%d_%H%M%S).log" | |
| readonly ADB_TIMEOUT=15 | |
| readonly ADB_LONG_TIMEOUT=120 | |
| # Stan połączenia | |
| DEVICE_SERIAL="" | |
| DEVICE_MODEL="" | |
| DEVICE_ANDROID="" | |
| DEVICE_BUILD="" | |
| DEVICE_SDK="" | |
| # Flaga — czy backup ustawień wykonany w tej sesji | |
| BACKUP_DONE=false | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 1 — INFRASTRUKTURA: LOGGING, UI, BŁĘDY | |
| # ═══════════════════════════════════════════════════════════════════ | |
| mkdir -p "$LOG_DIR" "$BACKUP_DIR" | |
| ts() { date '+%H:%M:%S'; } | |
| log() { printf "[%s] %s\n" "$(ts)" "$*" >> "$LOG_FILE"; } | |
| ok() { printf "${GREEN} ✓ %s${RESET}\n" "$*"; log "OK: $*"; } | |
| err() { printf "${BRED} ✗ %s${RESET}\n" "$*" >&2; log "ERROR: $*"; } | |
| warn() { printf "${BYELLOW} ⚠ %s${RESET}\n" "$*"; log "WARN: $*"; } | |
| info() { printf "${CYAN} ℹ %s${RESET}\n" "$*"; log "INFO: $*"; } | |
| step() { printf "${BOLD}${WHITE} ▶ %s${RESET}\n" "$*"; log "STEP: $*"; } | |
| detail() { printf "${DIM} %s${RESET}\n" "$*"; log "DETAIL: $*"; } | |
| header() { | |
| local txt="$1" | |
| local width=60 | |
| local pad=$(( (width - ${#txt} - 2) / 2 )) | |
| echo | |
| printf "${BOLD}${BLUE}" | |
| printf '╔'; printf '═%.0s' $(seq 1 $((width-2))); printf '╗\n' | |
| printf '║%*s%s%*s║\n' $pad "" "$txt" $((width - pad - ${#txt} - 2)) "" | |
| printf '╚'; printf '═%.0s' $(seq 1 $((width-2))); printf '╝\n' | |
| printf "${RESET}" | |
| } | |
| sep() { printf "${BLUE}%s${RESET}\n" "$(printf '─%.0s' {1..60})"; } | |
| sep_thin() { printf "${DIM}%s${RESET}\n" "$(printf '·%.0s' {1..60})"; } | |
| status_row() { | |
| # status_row "Klucz" "Wartość" [kolor_wartości] | |
| local key="$1" val="$2" color="${3:-$CYAN}" | |
| printf " ${DIM}%-32s${RESET} ${color}%s${RESET}\n" "${key}:" "$val" | |
| } | |
| pause() { | |
| echo | |
| read -rp "$(printf " ${YELLOW}Naciśnij [Enter] aby kontynuować...${RESET}")" _ | |
| } | |
| confirm() { | |
| # confirm "Treść pytania" → 0 jeśli tak, 1 jeśli nie | |
| local msg="$1" | |
| local ans | |
| read -rp "$(printf " ${BYELLOW}%s [t/N]: ${RESET}" "$msg")" ans | |
| [[ "${ans,,}" == "t" || "${ans,,}" == "tak" ]] | |
| } | |
| spinner() { | |
| # spinner <pid> <wiadomość> | |
| local pid=$1 msg="$2" | |
| local spin=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') | |
| local i=0 | |
| while kill -0 "$pid" 2>/dev/null; do | |
| printf "\r ${CYAN}%s${RESET} %s " "${spin[$i]}" "$msg" | |
| i=$(( (i+1) % ${#spin[@]} )) | |
| sleep 0.1 | |
| done | |
| printf "\r%60s\r" " " | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 2 — WRAPPER ADB (bezpieczny, logujący) | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Wykonaj komendę adb shell — zwraca kod wyjścia | |
| adb_cmd() { | |
| local cmd="$*" | |
| log "ADB_CMD: $cmd" | |
| local output exit_code=0 | |
| output=$(timeout "$ADB_TIMEOUT" adb -s "$DEVICE_SERIAL" shell "$cmd" 2>&1) || exit_code=$? | |
| log "ADB_OUT[$exit_code]: $output" | |
| echo "$output" | |
| return $exit_code | |
| } | |
| # Wykonaj długą komendę adb shell (dexopt, compile, itp.) | |
| adb_long() { | |
| local cmd="$*" | |
| log "ADB_LONG: $cmd" | |
| timeout "$ADB_LONG_TIMEOUT" adb -s "$DEVICE_SERIAL" shell "$cmd" 2>&1 | tee -a "$LOG_FILE" | |
| return "${PIPESTATUS[0]}" | |
| } | |
| # Pobierz wartość bez stderr | |
| adb_get() { | |
| timeout "$ADB_TIMEOUT" adb -s "$DEVICE_SERIAL" shell "$@" 2>/dev/null | tr -d '\r' | |
| } | |
| # Pobierz prop systemowy | |
| get_prop() { | |
| adb_get getprop "$1" 2>/dev/null || echo "" | |
| } | |
| # Ustawianie settings z walidacją i informowaniem | |
| safe_setting() { | |
| local namespace="$1" key="$2" value="$3" desc="${4:-}" | |
| local old_val | |
| old_val=$(adb_get "settings get ${namespace} ${key}" 2>/dev/null || echo "null") | |
| # Zapisz do backupu | |
| echo "settings put ${namespace} ${key} ${old_val}" >> "${BACKUP_DIR}/settings_restore.sh" | |
| if adb_cmd "settings put ${namespace} ${key} ${value}" >/dev/null 2>&1; then | |
| local label="${desc:-${namespace}/${key}}" | |
| ok "${label}: ${DIM}${old_val}${RESET} → ${GREEN}${value}${RESET}" | |
| return 0 | |
| else | |
| err "Nie udało się ustawić ${namespace}/${key}=${value}" | |
| return 1 | |
| fi | |
| } | |
| # Sprawdź czy urządzenie odpowiada | |
| ping_device() { | |
| timeout 5 adb -s "$DEVICE_SERIAL" shell "echo ok" 2>/dev/null | grep -q "ok" | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 3 — BANER I POŁĄCZENIE | |
| # ═══════════════════════════════════════════════════════════════════ | |
| print_banner() { | |
| clear | |
| printf "${BOLD}${MAGENTA}" | |
| cat << 'BANNER' | |
| ╔══════════════════════════════════════════════════════════════╗ | |
| ║ ║ | |
| ║ ██╗ ██╗ █████╗ ████████╗ ██████╗██╗ ██╗ ██╗ ██╗ ║ | |
| ║ ██║ ██║██╔══██╗╚══██╔══╝██╔════╝██║ ██║ ██║ ██║ ║ | |
| ║ ██║ █╗ ██║███████║ ██║ ██║ ███████║ ███████║ ║ | |
| ║ ██║███╗██║██╔══██║ ██║ ██║ ██╔══██║ ╚════██║ ║ | |
| ║ ╚███╔███╔╝██║ ██║ ██║ ╚██████╗██║ ██║ ██║ ║ | |
| ║ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ║ | |
| ║ ║ | |
| ║ Samsung Galaxy Watch 4 — Ultra Optimizer Suite ║ | |
| ║ One UI 8.0 / WearOS 6.0 / Android 16 / SM-R870 ║ | |
| ╚══════════════════════════════════════════════════════════════╝ | |
| BANNER | |
| printf "${RESET}" | |
| printf " ${DIM}Wersja: ${VERSION} · ${SCRIPT_DATE} · Log: ${LOG_FILE}${RESET}\n" | |
| sep | |
| } | |
| check_adb_installed() { | |
| if ! command -v adb &>/dev/null; then | |
| err "ADB nie znalezione w PATH!" | |
| err "Zainstaluj: https://developer.android.com/tools/releases/platform-tools" | |
| exit 1 | |
| fi | |
| local adb_ver | |
| adb_ver=$(adb version 2>/dev/null | head -1) | |
| ok "$adb_ver" | |
| } | |
| connect_device() { | |
| header "POŁĄCZENIE Z ZEGARKIEM" | |
| # Sprawdź istniejące połączenia | |
| local existing | |
| existing=$(adb devices 2>/dev/null | grep -E "^[^\s]+\s+device$" | head -1 || true) | |
| if [[ -n "$existing" ]]; then | |
| DEVICE_SERIAL=$(echo "$existing" | awk '{print $1}') | |
| ok "Znaleziono aktywne połączenie: ${CYAN}${DEVICE_SERIAL}${RESET}" | |
| else | |
| echo | |
| info "Brak aktywnych połączeń. Podaj dane zegarka:" | |
| echo | |
| read -rp "$(printf " ${CYAN}Adres IP zegarka: ${RESET}")" WATCH_IP | |
| if ! [[ "$WATCH_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then | |
| err "Nieprawidłowy format IP."; exit 1 | |
| fi | |
| read -rp "$(printf " ${CYAN}Port [domyślnie 5555]: ${RESET}")" WATCH_PORT | |
| WATCH_PORT="${WATCH_PORT:-5555}" | |
| DEVICE_SERIAL="${WATCH_IP}:${WATCH_PORT}" | |
| info "Łączę z ${DEVICE_SERIAL}..." | |
| # Wymuś restart serwera ADB na wypadek zawieszenia | |
| adb kill-server &>/dev/null; sleep 1; adb start-server &>/dev/null | |
| local conn_out | |
| conn_out=$(adb connect "$DEVICE_SERIAL" 2>&1) | |
| log "ADB connect: $conn_out" | |
| if echo "$conn_out" | grep -qiE "connected to|already connected"; then | |
| ok "Połączono z ${DEVICE_SERIAL}" | |
| elif echo "$conn_out" | grep -qi "refused"; then | |
| err "Połączenie odrzucone — sprawdź:" | |
| err " 1. Zegarek i komputer w tej samej sieci Wi-Fi" | |
| err " 2. Ustawienia → Opcje programistyczne → Debugowanie ADB → WŁ" | |
| err " 3. Port ADB: Ustawienia → Opcje programistyczne → Debugowanie ADB po sieci" | |
| exit 1 | |
| else | |
| err "Nieznany błąd połączenia: $conn_out"; exit 1 | |
| fi | |
| fi | |
| # Weryfikacja stanu urządzenia | |
| local state | |
| state=$(timeout 5 adb -s "$DEVICE_SERIAL" get-state 2>&1 || echo "error") | |
| case "$state" in | |
| device) ok "Urządzenie gotowe (device)" ;; | |
| unauthorized) | |
| err "Nieautoryzowane! Zaakceptuj dialog na zegarku → uruchom skrypt ponownie." | |
| exit 1 ;; | |
| offline) | |
| err "Urządzenie offline. Sprawdź sieć lub uruchom: adb kill-server && adb start-server" | |
| exit 1 ;; | |
| *) | |
| err "Nieznany stan: $state"; exit 1 ;; | |
| esac | |
| # Pobierz informacje o urządzeniu | |
| DEVICE_MODEL=$(get_prop ro.product.model) | |
| DEVICE_ANDROID=$(get_prop ro.build.version.release) | |
| DEVICE_BUILD=$(get_prop ro.build.display.id) | |
| DEVICE_SDK=$(get_prop ro.build.version.sdk) | |
| local device_arch | |
| device_arch=$(get_prop ro.product.cpu.abi) | |
| sep | |
| status_row "Model" "$DEVICE_MODEL" | |
| status_row "Android" "$DEVICE_ANDROID (SDK $DEVICE_SDK)" | |
| status_row "Build ID" "$DEVICE_BUILD" | |
| status_row "Architektura" "$device_arch" | |
| status_row "Połączony przez" "$DEVICE_SERIAL" | |
| sep | |
| if ! echo "$DEVICE_MODEL" | grep -qi "R870\|Watch 4"; then | |
| warn "Model ($DEVICE_MODEL) różni się od docelowego SM-R870." | |
| warn "Skrypt może wymagać dostosowania dla tego urządzenia." | |
| fi | |
| if (( DEVICE_SDK < 35 )); then | |
| warn "SDK ${DEVICE_SDK} — niektóre komendy ART/Android 16 mogą nie działać." | |
| fi | |
| # Utwórz skrypt przywracania backupu | |
| cat > "${BACKUP_DIR}/settings_restore.sh" << 'RESTORE_HEADER' | |
| #!/usr/bin/env bash | |
| # AUTO-WYGENEROWANY skrypt przywracania ustawień | |
| # Uruchom: adb -s DEVICE shell "sh /sdcard/watch4_restore.sh" | |
| echo "Przywracanie ustawień Watch4 Suite..." | |
| RESTORE_HEADER | |
| log "Sesja połączona: $DEVICE_SERIAL | Model: $DEVICE_MODEL | Android: $DEVICE_ANDROID" | |
| # ── KRYTYCZNA KONTROLA: /system pełny (wykryto na SM-R870 + One UI 8.0) ── | |
| local sys_avail | |
| sys_avail=$(adb_get "df /system 2>/dev/null | tail -1 | awk '{print \$4}'" | tr -d ' \r' || echo "") | |
| if [[ "$sys_avail" =~ ^[0-9]+$ ]]; then | |
| if (( sys_avail < 51200 )); then | |
| echo | |
| printf "${BRED}╔══════════════════════════════════════════════════════════╗\n" | |
| printf "║ ⚠ KRYTYCZNE: PARTYCJA /system PRAWIE PEŁNA! ║\n" | |
| printf "║ Wolne: %-8s KB — ART nie może kompilować DEX! ║\n" "$sys_avail" | |
| printf "║ Zalecane: [3] Wydajność → [4] Czyść cache, potem restart ║\n" | |
| printf "╚══════════════════════════════════════════════════════════╝${RESET}\n" | |
| log "CRITICAL: /system available=${sys_avail} KB" | |
| fi | |
| fi | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 4 — DIAGNOSTYKA PROAKTYWNA | |
| # ═══════════════════════════════════════════════════════════════════ | |
| diagnostics_menu() { | |
| while true; do | |
| header "DIAGNOSTYKA PROAKTYWNA" | |
| echo " [1] Pełny raport systemu (snapshot)" | |
| echo " [2] Monitor temperatury w czasie rzeczywistym" | |
| echo " [3] Analiza zużycia RAM i procesów" | |
| echo " [4] Inspekcja baterii (health, wear, temperatury)" | |
| echo " [5] Analiza wydajności UI (jank, frame drops)" | |
| echo " [6] Sprawdzenie usług systemowych (status krytycznych)" | |
| echo " [7] Analiza logów systemowych (ANR, crash, OOM)" | |
| echo " [8] Inspekcja stanu sieci i Bluetooth" | |
| echo " [9] Eksportuj pełny raport diagnostyczny do pliku" | |
| echo " [0] Powrót" | |
| sep | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) diag_full_snapshot ;; | |
| 2) diag_thermal_monitor ;; | |
| 3) diag_ram_analysis ;; | |
| 4) diag_battery_health ;; | |
| 5) diag_ui_performance ;; | |
| 6) diag_system_services ;; | |
| 7) diag_log_analysis ;; | |
| 8) diag_network_bt ;; | |
| 9) diag_export_report ;; | |
| 0) return ;; | |
| *) warn "Nieprawidłowy wybór." ;; | |
| esac | |
| done | |
| } | |
| diag_full_snapshot() { | |
| header "PEŁNY RAPORT SYSTEMU" | |
| sep_thin | |
| step "Informacje systemowe" | |
| status_row "Model" "$(get_prop ro.product.model)" | |
| status_row "Android" "$(get_prop ro.build.version.release)" | |
| status_row "SDK" "$(get_prop ro.build.version.sdk)" | |
| status_row "Build" "$(get_prop ro.build.display.id)" | |
| status_row "CPU ABI" "$(get_prop ro.product.cpu.abi)" | |
| status_row "Chipset" "$(get_prop ro.hardware)" | |
| status_row "Uptime" "$(adb_get cat /proc/uptime | awk '{printf "%d min %d sek", $1/60, $1%60}')" | |
| sep_thin | |
| step "Pamięć RAM" | |
| local mem_total mem_avail mem_free mem_commit swap_total swap_free slab_unreclaim | |
| mem_total=$(adb_get "grep MemTotal /proc/meminfo | awk '{print \$2}'") | |
| mem_avail=$(adb_get "grep MemAvailable /proc/meminfo | awk '{print \$2}'") | |
| mem_commit=$(adb_get "grep Committed_AS /proc/meminfo | awk '{print \$2}'" || echo "0") | |
| swap_total=$(adb_get "grep SwapTotal /proc/meminfo | awk '{print \$2}'" || echo "0") | |
| swap_free=$(adb_get "grep SwapFree /proc/meminfo | awk '{print \$2}'" || echo "0") | |
| slab_unreclaim=$(adb_get "grep SUnreclaim /proc/meminfo | awk '{print \$2}'" || echo "0") | |
| local mem_used=$(( (mem_total - mem_avail) / 1024 )) | |
| local mem_total_mb=$(( mem_total / 1024 )) | |
| local mem_avail_mb=$(( mem_avail / 1024 )) | |
| local commit_mb=$(( ${mem_commit:-0} / 1024 )) | |
| local swap_used_mb=$(( (${swap_total:-0} - ${swap_free:-0}) / 1024 )) | |
| local slab_mb=$(( ${slab_unreclaim:-0} / 1024 )) | |
| local ram_color="$GREEN" | |
| (( mem_avail_mb < 250 )) && ram_color="$YELLOW" | |
| (( mem_avail_mb < 100 )) && ram_color="$RED" | |
| status_row "RAM łącznie" "${mem_total_mb} MB" | |
| status_row "RAM dostępna" "${mem_avail_mb} MB" "$ram_color" | |
| status_row "RAM użyta (est.)" "${mem_used} MB" | |
| status_row "SWAP/ZRAM użyty" "${swap_used_mb} MB / $(( ${swap_total:-0} / 1024 )) MB" | |
| status_row "Kernel SUnreclaim" "${slab_mb} MB" | |
| if [[ "$commit_mb" -gt 0 && "$mem_total_mb" -gt 0 ]] 2>/dev/null; then | |
| local overcommit_ratio | |
| overcommit_ratio=$(awk "BEGIN {printf \"%.1f\", ${commit_mb} / ${mem_total_mb}}") | |
| status_row "Committed_AS" "${commit_mb} MB (${overcommit_ratio}x fizycznego RAM)" | |
| local oc_int | |
| oc_int=$(awk "BEGIN {printf \"%d\", ${commit_mb} / ${mem_total_mb}}") | |
| (( oc_int > 10 )) && warn "Overcommit >${oc_int}x — LMK agresywnie zabija procesy tła!" | |
| fi | |
| sep_thin | |
| step "Miejsce na dysku" | |
| adb_get "df /data /cache /system 2>/dev/null" | while IFS= read -r line; do | |
| printf " ${DIM}%s${RESET}\n" "$line" | |
| done | |
| sep_thin | |
| step "Temperatura CPU (przybliżona)" | |
| local temps | |
| temps=$(adb_get "cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null | head -5" || echo "niedostępne") | |
| echo "$temps" | head -5 | while IFS= read -r t; do | |
| if [[ "$t" =~ ^[0-9]+$ ]]; then | |
| local deg=$(( t / 1000 )) | |
| local color="$GREEN" | |
| (( deg > 45 )) && color="$YELLOW" | |
| (( deg > 55 )) && color="$RED" | |
| printf " ${color}%d°C${RESET}\n" "$deg" | |
| fi | |
| done | |
| sep_thin | |
| step "Akumulator" | |
| adb_get "dumpsys battery 2>/dev/null" | grep -E "level|status|health|temperature|voltage|plugged" | while IFS= read -r line; do | |
| printf " ${CYAN}%s${RESET}\n" "$line" | |
| done | |
| sep_thin | |
| step "Animacje systemowe" | |
| for key in window_animation_scale transition_animation_scale animator_duration_scale; do | |
| local val | |
| val=$(adb_get "settings get global $key") | |
| status_row "$key" "$val" | |
| done | |
| step "AOD Status" | |
| local aod_val | |
| aod_val=$(adb_get "settings get secure doze_always_on") | |
| local aod_label="Wyłączone" | |
| [[ "$aod_val" == "1" ]] && aod_label="Włączone" | |
| status_row "doze_always_on" "$aod_val ($aod_label)" | |
| pause | |
| } | |
| diag_thermal_monitor() { | |
| header "MONITOR TEMPERATURY CZASU RZECZYWISTEGO" | |
| echo -e " ${YELLOW}Monitorowanie przez 30 sekund. Ctrl+C aby przerwać wcześniej.${RESET}" | |
| echo | |
| local zones | |
| zones=$(adb_get "ls /sys/class/thermal/ 2>/dev/null" | grep thermal_zone | head -8) | |
| for i in $(seq 1 30); do | |
| printf "\r ${BOLD}[%2d/30s]${RESET} " "$i" | |
| while IFS= read -r zone; do | |
| local temp | |
| temp=$(adb_get "cat /sys/class/thermal/${zone}/temp 2>/dev/null" || echo "0") | |
| if [[ "$temp" =~ ^[0-9]+$ ]]; then | |
| local deg=$(( temp / 1000 )) | |
| local color="$GREEN" | |
| (( deg > 40 )) && color="$YELLOW" | |
| (( deg > 50 )) && color="$RED" | |
| printf "${color}%d°C${RESET} " "$deg" | |
| fi | |
| done <<< "$zones" | |
| sleep 1 | |
| done | |
| echo | |
| ok "Monitorowanie zakończone." | |
| pause | |
| } | |
| diag_ram_analysis() { | |
| header "ANALIZA RAM I PROCESÓW" | |
| sep_thin | |
| step "Top 10 procesów według RAM" | |
| echo | |
| printf " ${BOLD}%-8s %-12s %-8s %s${RESET}\n" "PID" "RSS(MB)" "USER" "NAZWA" | |
| sep_thin | |
| adb_get "ps -eo pid,rss,user,comm 2>/dev/null | sort -k2 -rn | head -10" | while IFS= read -r line; do | |
| local pid rss user comm | |
| read -r pid rss user comm <<< "$line" | |
| if [[ "$rss" =~ ^[0-9]+$ ]]; then | |
| local rss_mb=$(( rss / 1024 )) | |
| local color="$WHITE" | |
| (( rss_mb > 100 )) && color="$YELLOW" | |
| (( rss_mb > 200 )) && color="$RED" | |
| printf " ${DIM}%-8s${RESET} ${color}%-12s${RESET} %-8s %s\n" "$pid" "${rss_mb} MB" "$user" "$comm" | |
| fi | |
| done | |
| sep_thin | |
| step "Statystyki LMK (Low Memory Killer)" | |
| adb_get "cat /sys/module/lowmemorykiller/parameters/minfree 2>/dev/null" | while IFS= read -r line; do | |
| detail "LMK minfree: $line" | |
| done | |
| sep_thin | |
| step "Ostatnie zabite procesy (OOM)" | |
| adb_get "logcat -d -t 50 --pid 1 2>/dev/null" | grep -i "lowmem\|oom_kill\|killing\|killed" | tail -5 | while IFS= read -r line; do | |
| printf " ${RED}%s${RESET}\n" "$line" | |
| done || detail "Brak wpisów OOM w ostatnich logach." | |
| pause | |
| } | |
| diag_battery_health() { | |
| header "INSPEKCJA BATERII" | |
| sep_thin | |
| step "Podstawowe dane" | |
| adb_get "dumpsys battery 2>/dev/null" | while IFS= read -r line; do | |
| local key val | |
| IFS=':' read -r key val <<< "$line" | |
| key=$(echo "$key" | xargs) | |
| val=$(echo "$val" | xargs) | |
| [[ -z "$key" || -z "$val" ]] && continue | |
| case "$key" in | |
| level) | |
| local color="$GREEN" | |
| (( val < 30 )) && color="$YELLOW" | |
| (( val < 15 )) && color="$RED" | |
| status_row "Poziom" "${val}%" "$color" | |
| ;; | |
| health) | |
| local h_label | |
| case "$val" in | |
| 2) h_label="Dobry ✓" ;; | |
| 3) h_label="Przegrzanie !" ;; | |
| 4) h_label="Martwy ✗" ;; | |
| *) h_label="Status: $val" ;; | |
| esac | |
| status_row "Kondycja" "$h_label" | |
| ;; | |
| temperature) | |
| local temp_c | |
| temp_c=$(echo "scale=1; $val / 10" | bc 2>/dev/null || echo "${val}/10") | |
| status_row "Temperatura" "${temp_c}°C" | |
| ;; | |
| voltage) status_row "Napięcie" "${val} mV" ;; | |
| status) | |
| local s_label | |
| case "$val" in | |
| 1) s_label="Nieznany" ;; 2) s_label="Ładowanie" ;; | |
| 3) s_label="Rozładowywanie" ;; 5) s_label="Pełny" ;; | |
| *) s_label="$val" ;; | |
| esac | |
| status_row "Status" "$s_label" | |
| ;; | |
| *) status_row "$key" "$val" ;; | |
| esac | |
| done | |
| sep_thin | |
| step "Historia zużycia baterii (ostatnie 24h)" | |
| adb_get "dumpsys batterystats 2>/dev/null" | grep -E "Discharge|charge|plugged|screen" | head -10 | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| sep_thin | |
| step "Rekomendacje" | |
| local level | |
| level=$(adb_get "dumpsys battery 2>/dev/null | grep level | awk -F: '{print \$2}' | tr -d ' \r'") | |
| if [[ "$level" =~ ^[0-9]+$ ]]; then | |
| (( level < 20 )) && warn "Niski poziom baterii — podłącz ładowarkę przed dalszą optymalizacją." | |
| (( level >= 20 )) && ok "Poziom baterii wystarczający do optymalizacji." | |
| fi | |
| pause | |
| } | |
| diag_ui_performance() { | |
| header "ANALIZA WYDAJNOŚCI UI (JANK / FRAME DROPS)" | |
| echo -e " ${YELLOW}Mierzę wydajność renderowania — dotknij ekranu zegarka podczas testu.${RESET}" | |
| step "Reset statystyk Gfxinfo" | |
| adb_cmd "dumpsys gfxinfo --reset" >/dev/null 2>&1 | |
| sleep 2 | |
| step "Czekam 5 sekund na zbieranie danych..." | |
| sleep 5 | |
| step "Odczyt statystyk klatek (SurfaceFlinger)" | |
| local gfx_out | |
| gfx_out=$(adb_get "dumpsys gfxinfo 2>/dev/null" | head -60) | |
| local total_frames janky_frames slow_frames | |
| total_frames=$(echo "$gfx_out" | grep -i "total frames" | awk '{print $NF}' | head -1) | |
| janky_frames=$(echo "$gfx_out" | grep -i "janky frames" | awk '{print $NF}' | head -1) | |
| slow_frames=$(echo "$gfx_out" | grep -i ">16ms" | awk '{print $NF}' | head -1) | |
| if [[ -n "$total_frames" && "$total_frames" -gt 0 ]] 2>/dev/null; then | |
| status_row "Całkowita liczba klatek" "$total_frames" | |
| # BUGFIX v3.1: gfxinfo zwraca float (np. "43.48%") — użyj awk zamiast $(( )) | |
| local janky_clean | |
| janky_clean=$(echo "${janky_frames:-0}" | grep -oE '[0-9]+' | head -1) | |
| local jank_pct_raw | |
| jank_pct_raw=$(awk "BEGIN {printf \"%.1f\", ${janky_clean:-0} * 100 / ${total_frames}}") | |
| local jank_pct_int | |
| jank_pct_int=$(awk "BEGIN {printf \"%d\", ${janky_clean:-0} * 100 / ${total_frames}}") | |
| status_row "Klatki z jankiem (>16ms)" "${janky_clean:-?} (${jank_pct_raw}%)" | |
| local color="$GREEN" | |
| (( jank_pct_int > 5 )) && color="$YELLOW" | |
| (( jank_pct_int > 15 )) && color="$RED" | |
| status_row "Procent janku" "${jank_pct_raw}%" "$color" | |
| (( jank_pct_int > 10 )) && warn "Wykryto znaczny jank (${jank_pct_raw}%) — zalecana optymalizacja animacji i ART." | |
| (( jank_pct_int <= 5 )) && ok "Wydajność UI dobra (jank ${jank_pct_raw}%)." | |
| else | |
| info "Niewystarczające dane z gfxinfo. Sprawdź SurfaceFlinger:" | |
| adb_get "dumpsys SurfaceFlinger --latency 2>/dev/null" | head -15 | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| fi | |
| sep_thin | |
| step "Dispatching latency (Input)" | |
| adb_get "dumpsys input 2>/dev/null" | grep -E "latency|delay|drop" | head -5 | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| pause | |
| } | |
| diag_system_services() { | |
| header "STATUS USŁUG SYSTEMOWYCH" | |
| local critical_services=( | |
| "SurfaceFlinger:Rendering/UI" | |
| "WindowManagerService:Zarządzanie oknami" | |
| "ActivityManagerService:Zarządzanie aplikacjami" | |
| "PackageManagerService:Zarządzanie pakietami" | |
| "PowerManagerService:Zarządzanie energią" | |
| "ThermalManagerService:Zarządzanie cieplne" | |
| "WifiService:Wi-Fi" | |
| "BluetoothManagerService:Bluetooth" | |
| ) | |
| sep_thin | |
| printf " ${BOLD}%-28s %-20s %s${RESET}\n" "USŁUGA" "OPIS" "STATUS" | |
| sep_thin | |
| for entry in "${critical_services[@]}"; do | |
| local svc="${entry%%:*}" | |
| local desc="${entry##*:}" | |
| local found | |
| found=$(adb_get "service check ${svc} 2>/dev/null" | grep -ic "found" || echo "0") | |
| if [[ "$found" -gt 0 ]] 2>/dev/null; then | |
| printf " ${BOLD}%-28s${RESET} %-20s ${GREEN}● Aktywna${RESET}\n" "$svc" "$desc" | |
| else | |
| printf " ${BOLD}%-28s${RESET} %-20s ${RED}✗ Nieaktywna${RESET}\n" "$svc" "$desc" | |
| fi | |
| done | |
| sep_thin | |
| step "Procesy w stanie ZOMBIE lub DEAD" | |
| adb_get "ps -A 2>/dev/null | grep -E 'Z|D' | grep -v grep | head -10" | while IFS= read -r line; do | |
| warn "$line" | |
| done || detail "Brak procesów zombie." | |
| pause | |
| } | |
| diag_log_analysis() { | |
| header "ANALIZA LOGÓW SYSTEMOWYCH" | |
| step "ANR (Application Not Responding) — ostatnie zdarzenia" | |
| adb_get "logcat -d -t 200 2>/dev/null" | grep -i "ANR\|anr" | tail -5 | while IFS= read -r line; do | |
| printf " ${RED}%s${RESET}\n" "$line" | |
| done || detail "Brak ANR w buforze logów." | |
| sep_thin | |
| step "Crash / Wyjątki (ostatnie 5)" | |
| adb_get "logcat -d -t 200 2>/dev/null" | grep -iE "FATAL|crash|exception|NullPointer" | tail -5 | while IFS= read -r line; do | |
| printf " ${BRED}%s${RESET}\n" "$line" | |
| done || detail "Brak crashy w buforze." | |
| sep_thin | |
| step "OOM / Duszenie pamięci" | |
| adb_get "logcat -d -t 200 2>/dev/null" | grep -iE "lowmem|oom|kill" | tail -5 | while IFS= read -r line; do | |
| printf " ${YELLOW}%s${RESET}\n" "$line" | |
| done || detail "Brak wpisów OOM." | |
| sep_thin | |
| step "Throttling termiczny" | |
| adb_get "logcat -d -t 200 2>/dev/null" | grep -iE "thermal|throttl|overheat" | tail -5 | while IFS= read -r line; do | |
| printf " ${YELLOW}%s${RESET}\n" "$line" | |
| done || detail "Brak wpisów throttlingu." | |
| sep_thin | |
| step "Błędy SurfaceFlinger / HWUI" | |
| adb_get "logcat -d -t 200 -s SurfaceFlinger:W HWUI:W 2>/dev/null" | tail -10 | while IFS= read -r line; do | |
| printf " ${YELLOW}%s${RESET}\n" "$line" | |
| done || detail "Brak błędów renderowania." | |
| pause | |
| } | |
| diag_network_bt() { | |
| header "SIEĆ I BLUETOOTH" | |
| step "Status Wi-Fi" | |
| adb_get "dumpsys wifi 2>/dev/null" | grep -E "SSID|BSSID|signal|state|freq|speed|IP" | head -10 | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| sep_thin | |
| step "Status Bluetooth" | |
| adb_get "dumpsys bluetooth_manager 2>/dev/null" | grep -E "state|enabled|profile|address" | head -8 | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| sep_thin | |
| step "Połączone urządzenia BT" | |
| adb_get "dumpsys bluetooth_manager 2>/dev/null" | grep -E "name|class|connected" | head -6 | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| pause | |
| } | |
| diag_export_report() { | |
| local report_file="${LOG_DIR}/diagnostic_report_$(date +%Y%m%d_%H%M%S).txt" | |
| step "Generuję pełny raport diagnostyczny..." | |
| { | |
| echo "=== Watch4 Ultra Suite — Raport Diagnostyczny ===" | |
| echo "Data: $(date)" | |
| echo "Urządzenie: ${DEVICE_SERIAL}" | |
| echo | |
| echo "--- SYSTEM INFO ---" | |
| adb_get "getprop" 2>/dev/null | grep -E "ro\.(product|build|hardware|system)" | head -30 | |
| echo | |
| echo "--- MEMINFO ---" | |
| adb_get "cat /proc/meminfo" | |
| echo | |
| echo "--- BATTERY ---" | |
| adb_get "dumpsys battery" | |
| echo | |
| echo "--- GFXINFO ---" | |
| adb_get "dumpsys gfxinfo" | head -60 | |
| echo | |
| echo "--- TOP PROCESSES ---" | |
| adb_get "ps -eo pid,rss,comm | sort -k2 -rn | head -20" | |
| echo | |
| echo "--- THERMAL ---" | |
| adb_get "dumpsys thermalservice" 2>/dev/null | head -30 || echo "niedostępne" | |
| echo | |
| echo "--- LOGCAT ERRORS ---" | |
| adb_get "logcat -d -t 100 *:E" 2>/dev/null | head -50 | |
| } > "$report_file" & | |
| spinner $! "Zbieranie danych..." | |
| ok "Raport zapisany: ${report_file}" | |
| # Prześlij na zegarek (opcjonalnie) | |
| if confirm "Skopiować raport na zegarek (/sdcard/watch4_report.txt)?"; then | |
| adb push "$report_file" "/sdcard/watch4_report.txt" >/dev/null 2>&1 && ok "Raport na zegarku: /sdcard/watch4_report.txt" | |
| fi | |
| pause | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 5 — ZARZĄDZANIE AOD | |
| # ═══════════════════════════════════════════════════════════════════ | |
| aod_menu() { | |
| while true; do | |
| header "ZARZĄDZANIE AOD (Always-On Display)" | |
| # Status | |
| local aod_val doze_wakeup ambient_peekoff | |
| aod_val=$(adb_get "settings get secure doze_always_on") | |
| doze_wakeup=$(adb_get "settings get secure doze_wake_display_on_gesture") | |
| ambient_peekoff=$(adb_get "settings get secure doze_pick_up_gesture") | |
| status_row "AOD aktywne (doze_always_on)" "${aod_val:-null}" | |
| status_row "Wybudzenie gestem" "${doze_wakeup:-null}" | |
| status_row "Podniesienie nadgarstka" "${ambient_peekoff:-null}" | |
| sep_thin | |
| echo " [1] Wyłącz AOD (zalecane — eliminuje CPU spike przy wybudzeniu)" | |
| echo " [2] Włącz AOD" | |
| echo " [3] Tryb SMART — AOD tylko przy podniesieniu nadgarstka" | |
| echo " [4] Dezaktywuj komponenty Samsung AOD (agresywnie)" | |
| echo " [5] Przywróć komponenty AOD (cofnij opcję 4)" | |
| echo " [6] Zabij AmbientDreamProxy wakelock (NOWE — fix lagów wybudzenia)" | |
| echo " [7] Zarządzanie GPS/GNSS (wakeup killer)" | |
| echo " [8] Optymalizacja timeoutów ekranu" | |
| echo " [0] Powrót" | |
| sep | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) | |
| safe_setting "secure" "doze_always_on" "0" "AOD" | |
| safe_setting "secure" "doze_wake_display_on_gesture" "0" "Wybudzenie gestem" | |
| ok "AOD wyłączone. Eliminuje ~150-300ms opóźnienie wybudzenia Exynos W920." | |
| ;; | |
| 2) | |
| safe_setting "secure" "doze_always_on" "1" "AOD" | |
| ok "AOD włączone." | |
| ;; | |
| 3) | |
| safe_setting "secure" "doze_always_on" "0" "AOD" | |
| safe_setting "secure" "doze_wake_display_on_gesture" "1" "Wybudzenie gestem" | |
| safe_setting "secure" "doze_pick_up_gesture" "1" "Podniesienie nadgarstka" | |
| ok "Tryb SMART: AOD nieaktywne, ekran budzi się na gest/podniesienie." | |
| ;; | |
| 4) aod_disable_components ;; | |
| 5) aod_restore_components ;; | |
| 6) aod_kill_ambient_proxy ;; | |
| 7) aod_gps_control ;; | |
| 8) aod_screen_timeouts ;; | |
| 0) return ;; | |
| *) warn "Nieprawidłowy wybór." ;; | |
| esac | |
| pause | |
| done | |
| } | |
| # Pakiety Samsung AOD — bezpieczne do dezaktywacji (nie odinstalowania) | |
| declare -a SAMSUNG_AOD_COMPONENTS=( | |
| "com.samsung.android.app.aodservice" | |
| "com.samsung.systemui.aod" | |
| "com.samsung.android.app.watchface.aod" | |
| ) | |
| aod_disable_components() { | |
| header "DEZAKTYWACJA KOMPONENTÓW AOD" | |
| warn "Ta opcja dezaktywuje systemowe serwisy AOD Samsung." | |
| warn "System przełączy się na standardowy tryb doze WearOS." | |
| echo | |
| if ! confirm "Kontynuować?"; then | |
| info "Anulowano."; return | |
| fi | |
| for pkg in "${SAMSUNG_AOD_COMPONENTS[@]}"; do | |
| local pm_state | |
| pm_state=$(adb_get "pm list packages -d 2>/dev/null | grep ${pkg}" || echo "") | |
| if adb_cmd "pm disable-user --user 0 ${pkg}" >/dev/null 2>&1; then | |
| ok "Dezaktywowano: ${pkg}" | |
| else | |
| # Może już wyłączony lub nieistnieje | |
| warn "Pominięto (może nie istnieć): ${pkg}" | |
| fi | |
| done | |
| # Wymuś AOD standard WearOS | |
| safe_setting "secure" "doze_always_on" "0" "AOD (standard WearOS doze)" | |
| safe_setting "global" "always_finish_activities" "0" "Finalizacja aktywności" | |
| ok "Komponenty AOD Samsung dezaktywowane. Standardowy doze WearOS aktywny." | |
| info "Przywróć opcją [5] jeśli pojawią się problemy." | |
| } | |
| aod_restore_components() { | |
| header "PRZYWRACANIE KOMPONENTÓW AOD" | |
| for pkg in "${SAMSUNG_AOD_COMPONENTS[@]}"; do | |
| if adb_cmd "pm enable ${pkg}" >/dev/null 2>&1; then | |
| ok "Przywrócono: ${pkg}" | |
| else | |
| warn "Nie można przywrócić (lub nie istnieje): ${pkg}" | |
| fi | |
| done | |
| safe_setting "secure" "doze_always_on" "1" "AOD" | |
| ok "Komponenty AOD przywrócone." | |
| } | |
| aod_kill_ambient_proxy() { | |
| header "KILL AmbientDreamProxy WAKELOCK" | |
| echo -e " ${BYELLOW}Diagnoza z Twoich logów (14:02–14:03):${RESET}" | |
| echo -e " ${DIM}+wake_lock=u0a40:\"AmbientDreamProxy\" budzący urządzenie co ~10s${RESET}" | |
| echo -e " ${DIM}mimo doze_always_on=0. Powoduje lagi przy wybudzeniu + drenaż baterii.${RESET}" | |
| echo | |
| step "Zatrzymuję serwisy AmbientDreamProxy..." | |
| local proxy_pkgs=( | |
| "com.samsung.android.app.aodservice" | |
| "com.samsung.systemui.aod" | |
| "com.samsung.android.app.watchface.aod" | |
| ) | |
| for pkg in "${proxy_pkgs[@]}"; do | |
| if adb_cmd "am force-stop ${pkg}" >/dev/null 2>&1; then | |
| ok "force-stop: ${pkg}" | |
| else | |
| warn "Pominięto (może nie istnieć): ${pkg}" | |
| fi | |
| done | |
| step "Dezaktywuję wakelock trigger dla TILT gesture..." | |
| safe_setting "secure" "doze_wake_display_on_gesture" "0" "Wybudzenie gestem TILT" | |
| safe_setting "secure" "doze_pick_up_gesture" "0" "Podniesienie nadgarstka (doze)" | |
| safe_setting "secure" "doze_pulse_on_pick_up" "0" "Pulse on pick-up" | |
| safe_setting "secure" "doze_pulse_on_double_tap" "0" "Pulse on double-tap" | |
| step "Blokuję AmbientDreamProxy przez AppOps..." | |
| adb_cmd "appops set com.samsung.android.app.aodservice WAKE_LOCK deny" >/dev/null 2>&1 && \ | |
| ok "AppOps WAKE_LOCK: denied dla aodservice" || \ | |
| warn "AppOps — pominięto (może wymagać uprawnień)." | |
| step "Sprawdzam aktywne wakelocki po operacji..." | |
| local remaining | |
| remaining=$(adb_get "dumpsys power 2>/dev/null | grep -i 'AmbientDream'" || echo "") | |
| if [[ -z "$remaining" ]]; then | |
| ok "AmbientDreamProxy nie widoczny w aktywnych wakélockach." | |
| else | |
| warn "AmbientDreamProxy nadal obecny: ${remaining}" | |
| warn "Rozważ opcję [4] Dezaktywuj komponenty AOD dla trwałego efektu." | |
| fi | |
| info "Efekt utrzymuje się do następnego restartu. Dla trwałego — użyj opcji [4]." | |
| pause | |
| } | |
| aod_gps_control() { | |
| header "ZARZĄDZANIE GPS/GNSS (wakeup killer)" | |
| echo -e " ${BYELLOW}Diagnoza z Twoich logów:${RESET}" | |
| echo -e " ${DIM}wake_reason: \"12990000.gnss_mailbox\" — GNSS budzi CPU co kilka minut${RESET}" | |
| echo -e " ${DIM}nawet gdy żadna aplikacja nie używa GPS aktywnie.${RESET}" | |
| echo | |
| local gps_mode | |
| gps_mode=$(adb_get "settings get secure location_mode" || echo "?") | |
| local gps_label | |
| case "$gps_mode" in | |
| 0) gps_label="Wyłączone" ;; | |
| 1) gps_label="Tylko sensory (bez sieci)" ;; | |
| 2) gps_label="Tylko sieć (bez GPS)" ;; | |
| 3) gps_label="Wysoka dokładność (GPS + sieć)" ;; | |
| *) gps_label="Nieznany ($gps_mode)" ;; | |
| esac | |
| status_row "Obecny tryb lokalizacji" "$gps_label" | |
| echo | |
| echo " [1] Wyłącz GPS całkowicie (0) — eliminuje wakeup gnss_mailbox" | |
| echo " [2] Tylko sieć (2) — lokalizacja przez Wi-Fi bez GNSS hardware" | |
| echo " [3] Wysoka dokładność (3) — przywróć pełne GPS" | |
| echo " [4] Zatrzymaj proces GPS (tymczasowo)" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) | |
| safe_setting "secure" "location_mode" "0" "GPS (location_mode)" | |
| safe_setting "secure" "location_providers_allowed" "" "GPS providers" | |
| ok "GPS wyłączony. GNSS wakeup wyeliminowany." | |
| warn "Funkcje fitness wymagające GPS (np. trasy na świeżym powietrzu) będą niedostępne." | |
| ;; | |
| 2) | |
| safe_setting "secure" "location_mode" "2" "GPS (location_mode)" | |
| ok "Tryb sieciowy — lokalizacja bez budzenia GNSS hardware." | |
| ;; | |
| 3) | |
| safe_setting "secure" "location_mode" "3" "GPS (location_mode)" | |
| ok "Pełne GPS przywrócone." | |
| ;; | |
| 4) | |
| adb_cmd "am force-stop com.google.android.gms.location" >/dev/null 2>&1 || true | |
| adb_cmd "settings put secure location_providers_allowed -gps" >/dev/null 2>&1 | |
| ok "GPS tymczasowo zatrzymany — efekt do restartu urządzenia." | |
| ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| aod_screen_timeouts() { | |
| header "OPTYMALIZACJA TIMEOUTÓW EKRANU" | |
| local cur_timeout | |
| cur_timeout=$(adb_get "settings get system screen_off_timeout") | |
| status_row "Aktualny timeout" "${cur_timeout} ms ($(( ${cur_timeout:-15000} / 1000 ))s)" | |
| echo | |
| echo " [1] 5 sekund (agresywne oszczędzanie)" | |
| echo " [2] 10 sekund (zalecane dla WearOS)" | |
| echo " [3] 15 sekund (domyślne)" | |
| echo " [4] 30 sekund" | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) safe_setting "system" "screen_off_timeout" "5000" "Timeout ekranu" ;; | |
| 2) safe_setting "system" "screen_off_timeout" "10000" "Timeout ekranu" ;; | |
| 3) safe_setting "system" "screen_off_timeout" "15000" "Timeout ekranu" ;; | |
| 4) safe_setting "system" "screen_off_timeout" "30000" "Timeout ekranu" ;; | |
| *) warn "Nieprawidłowy wybór." ;; | |
| esac | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 6 — OPTYMALIZACJA WYDAJNOŚCI | |
| # ═══════════════════════════════════════════════════════════════════ | |
| performance_menu() { | |
| while true; do | |
| header "OPTYMALIZACJA WYDAJNOŚCI" | |
| echo " [1] Animacje systemowe" | |
| echo " [2] Renderer GPU (Exynos W920 — Vulkan/OpenGL)" | |
| echo " [3] Kompilacja ART / DEX (bg-dexopt, speed-profile)" | |
| echo " [4] Czyszczenie pamięci podręcznej" | |
| echo " [5] Zarządzanie CPU Governor" | |
| echo " [6] Optymalizacja Virtual Machine (Dalvik/ART flags)" | |
| echo " [7] Profil TURBO (wszystkie optymalizacje)" | |
| echo " [8] Przywróć ustawienia domyślne wydajności" | |
| echo " [0] Powrót" | |
| sep | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) manage_animations ;; | |
| 2) manage_gpu ;; | |
| 3) optimize_art ;; | |
| 4) clear_caches ;; | |
| 5) manage_cpu_governor ;; | |
| 6) optimize_vm_flags ;; | |
| 7) apply_turbo_profile ;; | |
| 8) restore_defaults_perf ;; | |
| 0) return ;; | |
| *) warn "Nieprawidłowy wybór." ;; | |
| esac | |
| done | |
| } | |
| manage_animations() { | |
| header "ANIMACJE SYSTEMOWE" | |
| local wa ts ad | |
| wa=$(adb_get "settings get global window_animation_scale") | |
| ts=$(adb_get "settings get global transition_animation_scale") | |
| ad=$(adb_get "settings get global animator_duration_scale") | |
| status_row "window_animation_scale" "$wa" | |
| status_row "transition_animation_scale" "$ts" | |
| status_row "animator_duration_scale" "$ad" | |
| sep_thin | |
| echo " [1] Wyłączone (0.0) — max responsywność, brak płynnych przejść" | |
| echo " [2] 0.3x — szybkie, prawie niezauważalne" | |
| echo " [3] 0.5x — zalecane dla WearOS" | |
| echo " [4] 1.0x — domyślne" | |
| echo " [5] Niestandardowe" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| set_animations() { | |
| local v="$1" | |
| safe_setting "global" "window_animation_scale" "$v" "window_animation_scale" | |
| safe_setting "global" "transition_animation_scale" "$v" "transition_animation_scale" | |
| safe_setting "global" "animator_duration_scale" "$v" "animator_duration_scale" | |
| } | |
| case "$ch" in | |
| 1) set_animations "0.0" ;; | |
| 2) set_animations "0.3" ;; | |
| 3) set_animations "0.5" ;; | |
| 4) set_animations "1.0" ;; | |
| 5) | |
| read -rp "$(printf " Podaj wartość: ")" cv | |
| [[ "$cv" =~ ^[0-9]+(\.[0-9]+)?$ ]] && set_animations "$cv" || err "Nieprawidłowa wartość." | |
| ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| manage_gpu() { | |
| header "RENDERER GPU (EXYNOS W920)" | |
| local cur_renderer | |
| cur_renderer=$(adb_get "getprop debug.hwui.renderer 2>/dev/null"; echo "") | |
| local cur_pipeline | |
| cur_pipeline=$(adb_get "getprop debug.hwui.use_vulkan 2>/dev/null"; echo "") | |
| status_row "debug.hwui.renderer" "${cur_renderer:-domyślny}" | |
| status_row "debug.hwui.use_vulkan" "${cur_pipeline:-domyślny}" | |
| sep_thin | |
| echo " [1] Skia Vulkan (skiavk) — zalecane Exynos W920 + Android 16" | |
| echo " [2] Skia OpenGL (skiagl) — stabilniejszy fallback" | |
| echo " [3] Wymuś Vulkan pipeline (hwui.use_vulkan=1)" | |
| echo " [4] Wyłącz debug overdraw" | |
| echo " [5] Przywróć domyślny renderer" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) | |
| adb_cmd "setprop debug.hwui.renderer skiavk" >/dev/null | |
| ok "Renderer: Skia Vulkan — restart aplikacji wymagany dla efektu." | |
| warn "Jeśli pojawią się artefakty graficzne → użyj opcji [5]." | |
| ;; | |
| 2) | |
| adb_cmd "setprop debug.hwui.renderer skiagl" >/dev/null | |
| ok "Renderer: Skia OpenGL." | |
| ;; | |
| 3) | |
| adb_cmd "setprop debug.hwui.use_vulkan 1" >/dev/null | |
| ok "Vulkan pipeline wymuszony." | |
| ;; | |
| 4) | |
| adb_cmd "setprop debug.hwui.overdraw false" >/dev/null | |
| adb_cmd "setprop debug.hwui.show_dirty_regions false" >/dev/null | |
| ok "Debug overdraw wyłączony." | |
| ;; | |
| 5) | |
| adb_cmd "setprop debug.hwui.renderer ''" >/dev/null | |
| adb_cmd "setprop debug.hwui.use_vulkan 0" >/dev/null | |
| ok "Renderer przywrócony do domyślnego." | |
| ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| optimize_art() { | |
| header "OPTYMALIZACJA ART / DEX (Android 16)" | |
| echo " [1] bg-dexopt-job (profile-guided — ZALECANE)" | |
| echo " [2] Kompilacja speed-profile dla wszystkich aplikacji" | |
| echo " [3] Kompilacja speed (pełna — długa, więcej RAM)" | |
| echo " [4] Reset i rekompilacja baseline (po aktualizacji systemu)" | |
| echo " [5] Wymuś GC (Garbage Collection) we wszystkich procesach" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) | |
| step "Uruchamiam bg-dexopt-job (profile-guided, może potrwać 3-8 min)..." | |
| warn "Nie blokuj ekranu i pozostań w zasięgu Wi-Fi." | |
| echo | |
| if adb_long "cmd package bg-dexopt-job"; then | |
| ok "bg-dexopt-job zakończony sukcesem." | |
| else | |
| warn "bg-dexopt-job zwrócił błąd — próbuję przez pm compile..." | |
| adb_long "pm compile -m speed-profile -a" && ok "pm compile zakończony." || err "Kompilacja nieudana — sprawdź log." | |
| fi | |
| ;; | |
| 2) | |
| step "Kompiluję wszystkie pakiety (speed-profile)..." | |
| adb_long "pm compile -m speed-profile -a" && ok "Zakończono." || err "Błąd kompilacji." | |
| ;; | |
| 3) | |
| warn "Kompilacja speed zajmie więcej czasu i wygeneruje duże pliki odex." | |
| confirm "Kontynuować?" || { info "Anulowano."; pause; return; } | |
| step "Kompilacja speed (pełna)..." | |
| adb_long "pm compile -m speed -a" && ok "Zakończono." || err "Błąd." | |
| ;; | |
| 4) | |
| warn "Reset profilów ART — system rekompiluje aplikacje stopniowo po restarcie." | |
| confirm "Kontynuować?" || { info "Anulowano."; pause; return; } | |
| adb_long "pm compile --reset -a" && ok "Profile ART zresetowane." || err "Błąd resetu." | |
| info "Zalecany restart zegarka dla efektu." | |
| ;; | |
| 5) | |
| step "Wymuszam GC we wszystkich procesach..." | |
| adb_cmd "am force-stop --user all && echo ok" >/dev/null 2>&1 || true | |
| local pids | |
| pids=$(adb_get "ps -A 2>/dev/null | grep -v root | awk '{print \$1}'" | head -30) | |
| local count=0 | |
| while IFS= read -r pid; do | |
| [[ "$pid" =~ ^[0-9]+$ ]] || continue | |
| adb_cmd "kill -10 $pid" >/dev/null 2>&1 && (( count++ )) || true | |
| done <<< "$pids" | |
| ok "GC wysłany do ${count} procesów." | |
| ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| clear_caches() { | |
| header "CZYSZCZENIE PAMIĘCI PODRĘCZNEJ" | |
| echo " [1] Trim caches aplikacji (pm trim-caches)" | |
| echo " [2] Wyczyść cache SurfaceFlinger" | |
| echo " [3] Wyczyść cache thumbnail / media" | |
| echo " [4] Wymuś trim /data/cache" | |
| echo " [5] Wszystkie powyższe" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| _trim_app_caches() { | |
| step "Przycinam cache aplikacji..." | |
| adb_cmd "pm trim-caches 4G" >/dev/null && ok "Cache aplikacji wyczyszczony." || err "pm trim-caches nieudane." | |
| } | |
| _sf_cache() { | |
| step "Restartuję SurfaceFlinger cache..." | |
| adb_cmd "service call SurfaceFlinger 1004 i32 0" >/dev/null 2>&1 && ok "SurfaceFlinger: odświeżony." || warn "SurfaceFlinger cache — pominięto (brak dostępu)." | |
| } | |
| _media_cache() { | |
| step "Czyszczę cache mediów..." | |
| adb_cmd "rm -rf /data/data/com.android.providers.media/cache/* 2>/dev/null" >/dev/null && ok "Cache mediów wyczyszczony." || warn "Cache mediów — brak dostępu (normalne bez root)." | |
| } | |
| _data_cache() { | |
| step "Wymuszam trim /data/cache..." | |
| adb_cmd "pm trim-caches 0" >/dev/null | |
| adb_cmd "sync" >/dev/null 2>&1 && ok "Sync i trim zakończony." || warn "Sync pominięty." | |
| } | |
| case "$ch" in | |
| 1) _trim_app_caches ;; | |
| 2) _sf_cache ;; | |
| 3) _media_cache ;; | |
| 4) _data_cache ;; | |
| 5) _trim_app_caches; _sf_cache; _media_cache; _data_cache ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| manage_cpu_governor() { | |
| header "CPU GOVERNOR (EXYNOS W920)" | |
| warn "Zmiana governor'a wymaga dostępu root lub odblokowanego jądra." | |
| warn "Na standardowym WearOS 6 ta opcja ogranicza się do odczytu." | |
| echo | |
| step "Odczyt obecnych ustawień CPU" | |
| local core=0 | |
| for governor_path in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do | |
| local gov | |
| gov=$(adb_get "cat ${governor_path} 2>/dev/null" || echo "niedostępny") | |
| local freq_cur | |
| freq_cur=$(adb_get "cat $(dirname "${governor_path}")/scaling_cur_freq 2>/dev/null || echo '?'") | |
| local freq_max | |
| freq_max=$(adb_get "cat $(dirname "${governor_path}")/scaling_max_freq 2>/dev/null || echo '?'") | |
| printf " ${CYAN}CPU%d${RESET}: Governor=${BOLD}%s${RESET} | Curr=%s kHz | Max=%s kHz\n" \ | |
| "$core" "$gov" "$freq_cur" "$freq_max" | |
| (( core++ )) | |
| (( core > 4 )) && break | |
| done | |
| sep_thin | |
| echo " [1] Ustaw governor 'performance' (próba bez root — może się nie udać)" | |
| echo " [2] Ustaw governor 'schedutil' (zalecany balans)" | |
| echo " [3] Odśwież odczyt stanu CPU" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) | |
| for cpu_path in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do | |
| adb_cmd "echo performance > ${cpu_path} 2>/dev/null" >/dev/null || true | |
| done | |
| ok "Próba ustawienia 'performance' — sprawdź odczyt opcją [3]." | |
| ;; | |
| 2) | |
| for cpu_path in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do | |
| adb_cmd "echo schedutil > ${cpu_path} 2>/dev/null" >/dev/null || true | |
| done | |
| ok "Próba ustawienia 'schedutil'." | |
| ;; | |
| 3) manage_cpu_governor; return ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| optimize_vm_flags() { | |
| header "OPTYMALIZACJA VM (DALVIK/ART FLAGS)" | |
| local current_heapsize | |
| current_heapsize=$(adb_get "getprop dalvik.vm.heapsize") | |
| status_row "dalvik.vm.heapsize" "${current_heapsize:-domyślny}" | |
| sep_thin | |
| echo " [1] Optymalne flagi VM dla WearOS (lepsza GC, mniejszy heap pressure)" | |
| echo " [2] Wymuś mały heap (agresywna GC — więcej wolnego RAM)" | |
| echo " [3] Przywróć domyślne flagi VM" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) | |
| # Flagi sprawdzone dla Android 14-16 WearOS | |
| adb_cmd "setprop dalvik.vm.heapgrowthlimit 48m" >/dev/null | |
| adb_cmd "setprop dalvik.vm.heapminfree 512k" >/dev/null | |
| adb_cmd "setprop dalvik.vm.heaptargetutilization 0.75" >/dev/null | |
| adb_cmd "setprop dalvik.vm.dex2oat-filter speed-profile" >/dev/null | |
| adb_cmd "setprop dalvik.vm.dex2oat-threads 2" >/dev/null | |
| ok "Flagi VM zoptymalizowane dla WearOS." | |
| ;; | |
| 2) | |
| adb_cmd "setprop dalvik.vm.heapgrowthlimit 32m" >/dev/null | |
| adb_cmd "setprop dalvik.vm.heapmaxfree 4m" >/dev/null | |
| adb_cmd "setprop dalvik.vm.heaptargetutilization 0.6" >/dev/null | |
| ok "Mały heap — więcej wolnego RAM, częstszy GC." | |
| ;; | |
| 3) | |
| adb_cmd "setprop dalvik.vm.heapgrowthlimit ''" >/dev/null | |
| adb_cmd "setprop dalvik.vm.heapminfree ''" >/dev/null | |
| adb_cmd "setprop dalvik.vm.heaptargetutilization ''" >/dev/null | |
| ok "Flagi VM przywrócone do domyślnych." | |
| ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| apply_turbo_profile() { | |
| header "PROFIL TURBO — KOMPLEKSOWA OPTYMALIZACJA" | |
| echo -e " ${BYELLOW}Wykona kolejno WSZYSTKIE bezpieczne optymalizacje:${RESET}" | |
| echo " → AOD wyłączone" | |
| echo " → AmbientDreamProxy wakelock zatrzymany" | |
| echo " → GPS tryb sieciowy (eliminuje gnss_mailbox wakeup)" | |
| echo " → WSZYSTKIE 3 animacje 0.5x (w tym window_animation_scale)" | |
| echo " → Renderer Skia Vulkan" | |
| echo " → Optymalne flagi VM" | |
| echo " → bg-dexopt-job (ART recompile)" | |
| echo " → Trim caches" | |
| echo | |
| confirm "Zastosować profil TURBO?" || { info "Anulowano."; pause; return; } | |
| sep | |
| step "1/8 — AOD..." | |
| safe_setting "secure" "doze_always_on" "0" "AOD" | |
| safe_setting "secure" "doze_wake_display_on_gesture" "0" "Wybudzenie gestem" | |
| safe_setting "secure" "doze_pick_up_gesture" "0" "Pulse on pick-up" | |
| safe_setting "secure" "doze_pulse_on_double_tap" "0" "Pulse on double-tap" | |
| step "2/8 — AmbientDreamProxy kill (fix lagów wybudzenia)..." | |
| for pkg in com.samsung.android.app.aodservice com.samsung.systemui.aod; do | |
| adb_cmd "am force-stop ${pkg}" >/dev/null 2>&1 && ok "force-stop: ${pkg}" || true | |
| done | |
| step "3/8 — GPS tryb sieciowy (eliminuje gnss_mailbox wakeup)..." | |
| safe_setting "secure" "location_mode" "2" "GPS location_mode (sieciowy)" | |
| step "4/8 — WSZYSTKIE animacje 0.5x..." | |
| # BUGFIX v3.1: ustawiamy wszystkie 3, łącznie z window (które było 1.0 w logach) | |
| safe_setting "global" "window_animation_scale" "0.5" "window_animation_scale" | |
| safe_setting "global" "transition_animation_scale" "0.5" "transition_animation_scale" | |
| safe_setting "global" "animator_duration_scale" "0.5" "animator_duration_scale" | |
| step "5/8 — Renderer GPU..." | |
| adb_cmd "setprop debug.hwui.renderer skiavk" >/dev/null && ok "Renderer: Skia Vulkan (Exynos W920)" | |
| step "6/8 — VM flags..." | |
| adb_cmd "setprop dalvik.vm.dex2oat-filter speed-profile" >/dev/null && ok "VM: speed-profile dex2oat" | |
| adb_cmd "setprop dalvik.vm.dex2oat-threads 2" >/dev/null && ok "VM: 2 dex2oat threads" | |
| step "7/8 — Cache trim..." | |
| adb_cmd "pm trim-caches 4G" >/dev/null && ok "Cache: wyczyszczony" | |
| step "8/8 — ART bg-dexopt-job (to zajmie chwilę)..." | |
| if adb_long "cmd package bg-dexopt-job" | tail -3; then | |
| ok "ART: bg-dexopt-job zakończony." | |
| else | |
| warn "bg-dexopt-job — próba pm compile..." | |
| adb_long "pm compile -m speed-profile -a" | tail -3 && ok "ART: pm compile OK." || err "ART: kompilacja nieudana." | |
| fi | |
| sep | |
| ok "Profil TURBO zastosowany!" | |
| info "Zalecany restart zegarka: opcja 'Naprawy → Restart'" | |
| pause | |
| } | |
| restore_defaults_perf() { | |
| header "PRZYWRACANIE DOMYŚLNYCH USTAWIEŃ WYDAJNOŚCI" | |
| confirm "Przywrócić domyślne ustawienia animacji, GPU, AOD?" || { info "Anulowano."; pause; return; } | |
| safe_setting "global" "window_animation_scale" "1.0" "window_animation_scale" | |
| safe_setting "global" "transition_animation_scale" "1.0" "transition_animation_scale" | |
| safe_setting "global" "animator_duration_scale" "1.0" "animator_duration_scale" | |
| safe_setting "secure" "doze_always_on" "1" "AOD" | |
| adb_cmd "setprop debug.hwui.renderer ''" >/dev/null && ok "Renderer: domyślny" | |
| adb_cmd "setprop debug.hwui.use_vulkan 0" >/dev/null && ok "Vulkan flag: wyłączony" | |
| ok "Ustawienia domyślne przywrócone." | |
| pause | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 7 — DEBLOAT | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # Katalog debloat — TYLKO aplikacje bezpieczne do wyłączenia (nie usunięcia) | |
| # Podzielone na kategorie z poziomem ryzyka | |
| declare -A DEBLOAT_PKGS_LOW=( | |
| # RYZYKO NISKIE — można bezpiecznie wyłączyć | |
| ["com.samsung.android.app.tips"]="Wskazówki Samsung" | |
| ["com.samsung.android.forest"]="Digital Wellbeing (ekran wellness)" | |
| ["com.samsung.android.game.gos"]="Game Optimizing Service" | |
| ["com.samsung.android.app.social"]="Samsung Social apps" | |
| ["com.sec.android.app.chromecustomtabs"]="Chrome Custom Tabs" | |
| ["com.samsung.systemui.bixby.wearable"]="Bixby Watch" | |
| ["com.samsung.android.bixby.agent"]="Bixby Agent" | |
| ["com.samsung.android.app.spage"]="Bixby Home" | |
| ["com.samsung.android.beaconmanager"]="Beacon Manager" | |
| ["com.samsung.android.dynamiclock"]="Dynamic Lock Screen" | |
| # Nowe — zidentyfikowane w logach 23.02.2026 jako hogi 40-50 MB | |
| ["com.samsung.android.easyMover"]="Easy Mover (~50 MB — transfer danych, zbędny na zegarku)" | |
| ["com.samsung.android.diagmonagent"]="DiagMonAgent (~41 MB — diagnostyka fabryczna)" | |
| ["com.samsung.android.app.reminder"]="Samsung Reminder (~42 MB)" | |
| ) | |
| declare -A DEBLOAT_PKGS_MEDIUM=( | |
| # RYZYKO ŚREDNIE — wyłącz ostrożnie | |
| ["com.samsung.android.app.reminder"]="Samsung Reminder" | |
| ["com.samsung.android.app.notes"]="Samsung Notes (watch)" | |
| ["com.samsung.android.app.samsungpay.wear"]="Samsung Pay Wear" | |
| ["com.samsung.android.app.watchmanager"]="Watch Manager (companion)" | |
| ["com.samsung.android.gearoplugin"]="Gear O Plugin" | |
| ["com.samsung.android.app.galaxyfind"]="Find My Watch (Podziel jeśli nieużywany)" | |
| ["com.samsung.android.mobileservice"]="Samsung Mobile Service" | |
| ) | |
| debloat_menu() { | |
| while true; do | |
| header "DEBLOAT — BEZPIECZNE ZARZĄDZANIE APLIKACJAMI" | |
| echo -e " ${GREEN}ZASADA: Tylko disable-user — żadnej aplikacji nie usuwamy!${RESET}" | |
| echo -e " ${GREEN}Wszystkie zmiany są w pełni odwracalne przez opcję [4].${RESET}" | |
| sep_thin | |
| echo " [1] Przejrzyj i wyłącz aplikacje NISKIE RYZYKO (zalecane)" | |
| echo " [2] Przejrzyj i wyłącz aplikacje ŚREDNIE RYZYKO (ostrożnie)" | |
| echo " [3] Pokaż wszystkie aktualnie wyłączone aplikacje" | |
| echo " [4] Przywróć wyłączone aplikacje Watch4 Suite" | |
| echo " [5] Skanuj zasoby pożerające aplikacje (top hog scan)" | |
| echo " [0] Powrót" | |
| sep | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) debloat_process_list "LOW" ;; | |
| 2) debloat_process_list "MEDIUM" ;; | |
| 3) debloat_show_disabled ;; | |
| 4) debloat_restore_all ;; | |
| 5) debloat_hog_scan ;; | |
| 0) return ;; | |
| *) warn "Nieprawidłowy wybór." ;; | |
| esac | |
| done | |
| } | |
| debloat_process_list() { | |
| local risk_level="$1" | |
| header "DEBLOAT — RYZYKO: ${risk_level}" | |
| # Wybierz odpowiedni słownik | |
| local -n pkg_map | |
| if [[ "$risk_level" == "LOW" ]]; then | |
| pkg_map=DEBLOAT_PKGS_LOW | |
| echo -e " ${GREEN}Aplikacje w tej kategorii są bezpieczne do wyłączenia.${RESET}" | |
| else | |
| pkg_map=DEBLOAT_PKGS_MEDIUM | |
| echo -e " ${YELLOW}Aplikacje w tej kategorii — wyłącz tylko jeśli NIE używasz ich funkcji.${RESET}" | |
| fi | |
| echo | |
| local disabled_log="${BACKUP_DIR}/debloat_${risk_level,,}.sh" | |
| echo "#!/usr/bin/env bash" > "$disabled_log" | |
| echo "# Przywracanie debloat ${risk_level}" >> "$disabled_log" | |
| local i=1 pkgs_arr=() | |
| for pkg in "${!pkg_map[@]}"; do | |
| pkgs_arr+=("$pkg") | |
| done | |
| for pkg in "${pkgs_arr[@]}"; do | |
| local desc="${pkg_map[$pkg]}" | |
| # Sprawdź czy zainstalowany | |
| local installed | |
| installed=$(adb_get "pm list packages 2>/dev/null | grep -c ${pkg}" || echo "0") | |
| local status_str | |
| local is_disabled | |
| is_disabled=$(adb_get "pm list packages -d 2>/dev/null | grep -c ${pkg}" || echo "0") | |
| if [[ "$installed" -lt 1 ]] 2>/dev/null; then | |
| status_str="${DIM}(nie zainstalowany)${RESET}" | |
| elif [[ "$is_disabled" -gt 0 ]] 2>/dev/null; then | |
| status_str="${YELLOW}(już wyłączony)${RESET}" | |
| else | |
| status_str="${GREEN}(aktywny)${RESET}" | |
| fi | |
| printf "\n [%2d] ${BOLD}%s${RESET}\n ${DIM}%s${RESET} %s\n" \ | |
| "$i" "$desc" "$pkg" "$status_str" | |
| (( i++ )) | |
| done | |
| echo | |
| sep_thin | |
| echo " Wpisz numery aplikacji do wyłączenia (np: 1 3 5) lub [A] dla wszystkich aktywnych," | |
| read -rp " lub [0] aby anulować: " selection | |
| [[ "$selection" == "0" ]] && return | |
| local selected_pkgs=() | |
| if [[ "${selection,,}" == "a" ]]; then | |
| selected_pkgs=("${pkgs_arr[@]}") | |
| else | |
| for num in $selection; do | |
| if [[ "$num" =~ ^[0-9]+$ ]] && (( num >= 1 && num <= ${#pkgs_arr[@]} )); then | |
| selected_pkgs+=("${pkgs_arr[$((num-1))]}") | |
| fi | |
| done | |
| fi | |
| if [[ ${#selected_pkgs[@]} -eq 0 ]]; then | |
| warn "Nie wybrano żadnych aplikacji."; pause; return | |
| fi | |
| echo | |
| warn "Zamierzam wyłączyć ${#selected_pkgs[@]} aplikację/aplikacje:" | |
| for pkg in "${selected_pkgs[@]}"; do | |
| printf " ${CYAN}%s${RESET} — %s\n" "$pkg" "${pkg_map[$pkg]}" | |
| done | |
| echo | |
| confirm "Potwierdzasz wyłączenie?" || { info "Anulowano."; pause; return; } | |
| echo | |
| for pkg in "${selected_pkgs[@]}"; do | |
| echo "pm enable ${pkg}" >> "$disabled_log" | |
| if adb_cmd "pm disable-user --user 0 ${pkg}" >/dev/null 2>&1; then | |
| ok "Wyłączono: ${pkg_map[$pkg]}" | |
| else | |
| warn "Pominięto (może nie istnieć na tym urządzeniu): ${pkg}" | |
| fi | |
| done | |
| echo | |
| ok "Gotowe. Skrypt przywracania: ${disabled_log}" | |
| info "Użyj opcji [4] w menu debloat aby przywrócić wszystko jednym kliknięciem." | |
| pause | |
| } | |
| debloat_show_disabled() { | |
| header "WYŁĄCZONE APLIKACJE (wszystkie)" | |
| local disabled | |
| disabled=$(adb_get "pm list packages -d 2>/dev/null" | sed 's/package://' | sort) | |
| if [[ -z "$disabled" ]]; then | |
| info "Brak wyłączonych aplikacji."; pause; return | |
| fi | |
| echo "$disabled" | while IFS= read -r pkg; do | |
| printf " ${YELLOW}●${RESET} %s\n" "$pkg" | |
| done | |
| echo | |
| info "Łącznie wyłączonych: $(echo "$disabled" | wc -l | tr -d ' ')" | |
| pause | |
| } | |
| debloat_restore_all() { | |
| header "PRZYWRACANIE APLIKACJI" | |
| info "Przywracam wszystkie aplikacje wyłączone przez Watch4 Suite..." | |
| local all_pkgs=("${!DEBLOAT_PKGS_LOW[@]}" "${!DEBLOAT_PKGS_MEDIUM[@]}") | |
| local restored=0 skipped=0 | |
| for pkg in "${all_pkgs[@]}"; do | |
| if adb_cmd "pm enable ${pkg}" >/dev/null 2>&1; then | |
| ok "Przywrócono: ${pkg}" | |
| (( restored++ )) | |
| else | |
| (( skipped++ )) | |
| fi | |
| done | |
| ok "Przywrócono: ${restored} | Pominięto (nie było wyłączone): ${skipped}" | |
| pause | |
| } | |
| debloat_hog_scan() { | |
| header "SKAN POŻERACZY ZASOBÓW" | |
| step "Analizuję zużycie CPU i RAM przez aplikacje (10 sekund)..." | |
| echo | |
| # CPU hogs | |
| printf " ${BOLD}TOP 10 PROCESÓW — RAM${RESET}\n" | |
| sep_thin | |
| printf " ${DIM}%-8s %-8s %s${RESET}\n" "PID" "RSS(MB)" "APLIKACJA" | |
| adb_get "ps -eo pid,rss,args 2>/dev/null | sort -k2 -rn | head -10" | while IFS= read -r line; do | |
| local pid rss app | |
| read -r pid rss app <<< "$line" | |
| if [[ "$rss" =~ ^[0-9]+$ ]]; then | |
| local rss_mb=$(( rss / 1024 )) | |
| local color="$WHITE" | |
| (( rss_mb > 80 )) && color="$YELLOW" | |
| (( rss_mb > 150 )) && color="$RED" | |
| printf " ${color}%-8s %-8s %s${RESET}\n" "$pid" "${rss_mb}MB" "${app:0:45}" | |
| fi | |
| done | |
| sep_thin | |
| step "Wakelock analysis (aplikacje budzące CPU)" | |
| adb_get "dumpsys power 2>/dev/null" | grep -E "PARTIAL_WAKE_LOCK|acquired|held" | head -10 | while IFS= read -r line; do | |
| printf " ${YELLOW}%s${RESET}\n" "$line" | |
| done || detail "Brak dostępnych wakelock." | |
| sep_thin | |
| step "Battery drain — wina aplikacji (BatteryStats)" | |
| adb_get "dumpsys batterystats --charged 2>/dev/null" | grep -E "Uid|wake|cpu" | head -15 | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| pause | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 8 — NAPRAWA SYSTEMU | |
| # ═══════════════════════════════════════════════════════════════════ | |
| repair_menu() { | |
| while true; do | |
| header "NAPRAWA SYSTEMU" | |
| echo " [1] Reset UI (SystemUI, Launcher — bez restartu urządzenia)" | |
| echo " [2] Reset stack Activity Manager (napraw zamrożone aplikacje)" | |
| echo " [3] Naprawa sieci Wi-Fi (reset stosu sieciowego)" | |
| echo " [4] Naprawa Bluetooth (reset stosu BT)" | |
| echo " [5] Wymuszenie synchonizacji zegara systemowego" | |
| echo " [6] Naprawa bazy danych PackageManager" | |
| echo " [7] Restart usług systemowych (bezpieczny)" | |
| echo " [8] Reset sensorów i health tracking" | |
| echo " [9] Pełny bezpieczny restart zegarka" | |
| echo " [10] Tryb odzyskiwania — eksport logów PRZED restartem" | |
| echo " [0] Powrót" | |
| sep | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) repair_ui_reset ;; | |
| 2) repair_activity_stack ;; | |
| 3) repair_wifi ;; | |
| 4) repair_bluetooth ;; | |
| 5) repair_time_sync ;; | |
| 6) repair_package_manager ;; | |
| 7) repair_services ;; | |
| 8) repair_sensors ;; | |
| 9) repair_safe_reboot ;; | |
| 10) repair_export_before_reboot ;; | |
| 0) return ;; | |
| *) warn "Nieprawidłowy wybór." ;; | |
| esac | |
| done | |
| } | |
| repair_ui_reset() { | |
| header "RESET UI SYSTEMU" | |
| warn "Ekran zegarka może migotać przez chwilę — to normalne." | |
| echo | |
| step "Restartuję SystemUI..." | |
| adb_cmd "am force-stop com.android.systemui" >/dev/null && ok "SystemUI zatrzymany." || warn "SystemUI — problem." | |
| sleep 2 | |
| # SystemUI restartuje się automatycznie; jeśli nie — poniżej | |
| adb_cmd "am startservice --user 0 -n com.android.systemui/.SystemUIService" >/dev/null 2>&1 || true | |
| sleep 2 | |
| step "Restartuję Launcher Samsung..." | |
| local launcher_pkg | |
| launcher_pkg=$(adb_get "pm list packages 2>/dev/null | grep -i 'launcher\|homescreen\|wlauncher'" | head -1 | sed 's/package://') | |
| if [[ -n "$launcher_pkg" ]]; then | |
| adb_cmd "am force-stop ${launcher_pkg}" >/dev/null && ok "Launcher zatrzymany: ${launcher_pkg}" | |
| else | |
| warn "Launcher nie zidentyfikowany." | |
| fi | |
| step "Restartuję usługę watchface..." | |
| adb_cmd "am force-stop com.samsung.android.app.watchface" >/dev/null 2>&1 && ok "Watchface service zrestartowany." || warn "Watchface service — pominięto." | |
| ok "Reset UI zakończony. Zegarek powinien odświeżyć interfejs." | |
| pause | |
| } | |
| repair_activity_stack() { | |
| header "RESET ACTIVITY MANAGER STACK" | |
| step "Czyszczę stos zadań Activity Managera..." | |
| adb_cmd "am clear-recent-tasks" >/dev/null 2>&1 && ok "Recent tasks wyczyszczone." || warn "Pominięto." | |
| step "Reset animacji przejść..." | |
| adb_cmd "wm dismiss-keyguard" >/dev/null 2>&1 || true | |
| sleep 1 | |
| adb_cmd "input keyevent KEYCODE_WAKEUP" >/dev/null 2>&1 || true | |
| step "Sprawdzam zakleszczone procesy..." | |
| adb_get "dumpsys activity processes 2>/dev/null" | grep -i "ANR\|LOCK\|WAIT" | head -5 | while IFS= read -r line; do | |
| warn "$line" | |
| done || ok "Brak zakleszczonych procesów." | |
| pause | |
| } | |
| repair_wifi() { | |
| header "NAPRAWA WI-FI" | |
| warn "Ta operacja zresetuje stos Wi-Fi — połączenie ADB chwilowo zerwane!" | |
| confirm "Kontynuować? (Skrypt automatycznie ponownie połączy)" || { info "Anulowano."; pause; return; } | |
| step "Zapisuję aktualny IP..." | |
| local saved_serial="$DEVICE_SERIAL" | |
| step "Wyłączam Wi-Fi..." | |
| adb_cmd "svc wifi disable" >/dev/null 2>&1 || true | |
| sleep 3 | |
| step "Włączam Wi-Fi..." | |
| adb_cmd "svc wifi enable" >/dev/null 2>&1 || true | |
| info "Czekam 10 sekund na reconnect..." | |
| sleep 10 | |
| step "Próba ponownego połączenia ADB..." | |
| local retries=5 | |
| while (( retries > 0 )); do | |
| if adb connect "$saved_serial" >/dev/null 2>&1 && ping_device; then | |
| DEVICE_SERIAL="$saved_serial" | |
| ok "Ponownie połączono z ${DEVICE_SERIAL}" | |
| break | |
| fi | |
| (( retries-- )) | |
| warn "Próba połączenia... pozostało ${retries}" | |
| sleep 3 | |
| done | |
| if (( retries == 0 )); then | |
| err "Nie udało się połączyć automatycznie." | |
| err "Uruchom skrypt ponownie po chwili." | |
| fi | |
| pause | |
| } | |
| repair_bluetooth() { | |
| header "NAPRAWA BLUETOOTH" | |
| step "Wyłączam Bluetooth..." | |
| adb_cmd "svc bluetooth disable" >/dev/null 2>&1 && ok "BT wyłączony." || warn "BT disable — pominięto." | |
| sleep 3 | |
| step "Włączam Bluetooth..." | |
| adb_cmd "svc bluetooth enable" >/dev/null 2>&1 && ok "BT włączony." || warn "BT enable — pominięto." | |
| sleep 2 | |
| step "Sprawdzam status BT..." | |
| local bt_state | |
| bt_state=$(adb_get "settings get global bluetooth_on 2>/dev/null") | |
| status_row "bluetooth_on" "${bt_state:-?}" | |
| ok "Reset Bluetooth zakończony." | |
| pause | |
| } | |
| repair_time_sync() { | |
| header "SYNCHRONIZACJA ZEGARA SYSTEMOWEGO" | |
| step "Obecny czas systemowy..." | |
| adb_get "date" | while IFS= read -r line; do detail "$line"; done | |
| step "Synchronizuję czas z NTP..." | |
| adb_cmd "settings put global ntp_server pool.ntp.org" >/dev/null && ok "NTP server: pool.ntp.org" | |
| adb_cmd "settings put global auto_time 1" >/dev/null && ok "Auto-time włączony" | |
| adb_cmd "settings put global auto_time_zone 1" >/dev/null && ok "Auto-timezone włączony" | |
| # Wymuś sync przez restart serwisu czasu | |
| adb_cmd "am broadcast -a android.intent.action.TIME_SET" >/dev/null 2>&1 && ok "TIME_SET broadcast wysłany." || warn "Broadcast — pominięto." | |
| step "Czas po synchronizacji:" | |
| sleep 2 | |
| adb_get "date" | while IFS= read -r line; do ok "$line"; done | |
| pause | |
| } | |
| repair_package_manager() { | |
| header "NAPRAWA BAZY DANYCH PACKAGEMANAGER" | |
| warn "Ta operacja wyczyści pamięć podręczną PackageManagera i zreoptymalizuje bazę." | |
| step "Sprawdzam integralność PackageManager..." | |
| local pm_err | |
| pm_err=$(adb_get "pm dump com.android.providers.settings 2>/dev/null | grep -i error | head -5" || echo "") | |
| [[ -n "$pm_err" ]] && warn "Wykryte błędy PM: $pm_err" || ok "PM integrity: brak widocznych błędów." | |
| step "Czyszczę cache PackageManager..." | |
| adb_cmd "pm clear com.android.providers.settings" >/dev/null 2>&1 && ok "Settings provider cache wyczyszczony." || warn "Pominięto." | |
| step "Rekompilacja zoptymalizowanego kodu PackageManager..." | |
| adb_cmd "pm compile -m speed-profile com.android.settings" >/dev/null 2>&1 && ok "Settings recompiled." || warn "Pominięto." | |
| step "Trim storage..." | |
| adb_cmd "pm trim-caches 4G" >/dev/null && ok "Storage trimmed." | |
| ok "Naprawa PM zakończona." | |
| pause | |
| } | |
| repair_services() { | |
| header "RESTART USŁUG SYSTEMOWYCH (BEZPIECZNY)" | |
| echo " [1] Restart SurfaceFlinger (napraw rendering)" | |
| echo " [2] Restart MediaServer (napraw audio/media)" | |
| echo " [3] Restart InputDispatcher (napraw dotyk)" | |
| echo " [4] Wyczyść cache i zrestartuj wszystkie kluczowe usługi" | |
| echo " [0] Powrót" | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) | |
| warn "Ekran może migać przez 2-5 sekund." | |
| adb_cmd "kill \$(pidof surfaceflinger)" >/dev/null 2>&1 | |
| sleep 3 && ok "SurfaceFlinger zrestartowany (automatycznie przez init)." | |
| ;; | |
| 2) | |
| adb_cmd "kill \$(pidof mediaserver)" >/dev/null 2>&1 | |
| sleep 2 && ok "MediaServer zrestartowany." | |
| ;; | |
| 3) | |
| adb_cmd "kill \$(pidof inputdispatcher)" >/dev/null 2>&1 | |
| sleep 1 && ok "InputDispatcher zrestartowany." | |
| ;; | |
| 4) | |
| warn "Restartuję kluczowe usługi — chwilowe zakłócenie UI." | |
| confirm "Kontynuować?" || { info "Anulowano."; return; } | |
| for svc_proc in surfaceflinger mediaserver; do | |
| adb_cmd "kill \$(pidof ${svc_proc} 2>/dev/null)" >/dev/null 2>&1 && ok "${svc_proc}: restarted" || warn "${svc_proc}: pominięto" | |
| sleep 1 | |
| done | |
| ;; | |
| 0) return ;; | |
| esac | |
| pause | |
| } | |
| repair_sensors() { | |
| header "RESET SENSORÓW I HEALTH TRACKING" | |
| step "Resetuję kalibrację sensorów..." | |
| adb_cmd "am broadcast -a android.intent.action.SENSOR_RECALIBRATION" >/dev/null 2>&1 || true | |
| adb_cmd "am force-stop com.samsung.android.wear.shealth" >/dev/null 2>&1 && ok "Samsung Health Watch zrestartowany." || warn "Samsung Health Watch — pominięto." | |
| adb_cmd "am force-stop com.samsung.android.service.health" >/dev/null 2>&1 && ok "Health Service zrestartowany." || warn "Health Service — pominięto." | |
| step "Sprawdzam sensory..." | |
| adb_get "dumpsys sensorservice 2>/dev/null | head -20" | while IFS= read -r line; do | |
| detail "$line" | |
| done | |
| ok "Reset sensorów zakończony. Pomiary zdrowia zostaną skalibrowane podczas kolejnego noszenia." | |
| pause | |
| } | |
| repair_safe_reboot() { | |
| header "BEZPIECZNY RESTART ZEGARKA" | |
| # Przed restartem — wykonaj backup | |
| if [[ "$BACKUP_DONE" == "false" ]]; then | |
| step "Tworzę backup ustawień przed restartem..." | |
| adb_get "settings list global" > "${BACKUP_DIR}/global_settings.txt" 2>/dev/null | |
| adb_get "settings list secure" > "${BACKUP_DIR}/secure_settings.txt" 2>/dev/null | |
| adb_get "settings list system" > "${BACKUP_DIR}/system_settings.txt" 2>/dev/null | |
| ok "Backup zapisany: ${BACKUP_DIR}/" | |
| BACKUP_DONE=true | |
| fi | |
| warn "Zegarek uruchomi się ponownie. Połączenie ADB zostanie zerwane." | |
| confirm "Uruchomić ponownie zegarek?" || { info "Anulowano."; pause; return; } | |
| info "Restartuję zegarek..." | |
| adb -s "$DEVICE_SERIAL" reboot | |
| ok "Restart zlecony." | |
| info "Zegarek będzie dostępny za ~60-90 sekund." | |
| info "Uruchom skrypt ponownie po restarcie." | |
| exit 0 | |
| } | |
| repair_export_before_reboot() { | |
| header "EKSPORT LOGÓW PRZED RESTARTEM" | |
| local export_dir="${LOG_DIR}/pre_reboot_$(date +%Y%m%d_%H%M%S)" | |
| mkdir -p "$export_dir" | |
| step "Eksportuje pełną diagnostykę..." | |
| { | |
| echo "=== PRE-REBOOT DUMP ===" | |
| echo "Date: $(date)" | |
| echo "Device: $DEVICE_SERIAL" | |
| adb_get "dumpsys" 2>/dev/null | |
| } > "${export_dir}/dumpsys_full.txt" & | |
| spinner $! "Zbieranie dumpsys (może potrwać 30s)..." | |
| adb_get "logcat -d 2>/dev/null" > "${export_dir}/logcat.txt" & | |
| spinner $! "Zbieranie logcat..." | |
| adb_get "getprop 2>/dev/null" > "${export_dir}/getprop.txt" | |
| ok "Eksport zakończony: ${export_dir}" | |
| info "Możesz teraz bezpiecznie wykonać restart zegarka." | |
| pause | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 9 — BACKUP / RESTORE PEŁNY | |
| # ═══════════════════════════════════════════════════════════════════ | |
| backup_menu() { | |
| while true; do | |
| header "BACKUP I PRZYWRACANIE" | |
| status_row "Katalog backup" "$BACKUP_DIR" | |
| sep_thin | |
| echo " [1] Backup wszystkich ustawień systemowych" | |
| echo " [2] Przywróć ustawienia z backupu tej sesji" | |
| echo " [3] Pokaż istniejące backupy" | |
| echo " [4] Eksportuj backup na zegarek (/sdcard/watch4_backup/)" | |
| echo " [0] Powrót" | |
| sep | |
| read -rp "$(printf " Wybór: ")" ch | |
| case "$ch" in | |
| 1) backup_settings ;; | |
| 2) restore_from_backup ;; | |
| 3) list_backups ;; | |
| 4) export_backup_to_watch ;; | |
| 0) return ;; | |
| *) warn "Nieprawidłowy wybór." ;; | |
| esac | |
| done | |
| } | |
| backup_settings() { | |
| step "Tworzę pełny backup ustawień systemowych..." | |
| adb_get "settings list global 2>/dev/null" | while IFS='=' read -r key val; do | |
| echo "settings put global '${key}' '${val}'" | |
| done > "${BACKUP_DIR}/restore_global.sh" | |
| adb_get "settings list secure 2>/dev/null" | while IFS='=' read -r key val; do | |
| echo "settings put secure '${key}' '${val}'" | |
| done > "${BACKUP_DIR}/restore_secure.sh" | |
| adb_get "settings list system 2>/dev/null" | while IFS='=' read -r key val; do | |
| echo "settings put system '${key}' '${val}'" | |
| done > "${BACKUP_DIR}/restore_system.sh" | |
| BACKUP_DONE=true | |
| ok "Backup zakończony: ${BACKUP_DIR}/" | |
| detail "restore_global.sh | restore_secure.sh | restore_system.sh" | |
| pause | |
| } | |
| restore_from_backup() { | |
| if [[ "$BACKUP_DONE" == "false" ]]; then | |
| warn "Nie wykonano backupu w tej sesji. Wykonaj najpierw opcję [1]."; pause; return | |
| fi | |
| step "Przywracam ustawienia z backupu..." | |
| for restore_script in "${BACKUP_DIR}"/restore_*.sh "${BACKUP_DIR}"/debloat_*.sh; do | |
| [[ -f "$restore_script" ]] || continue | |
| info "Przetwarzam: $(basename "$restore_script")" | |
| while IFS= read -r cmd; do | |
| [[ "$cmd" =~ ^# ]] && continue | |
| [[ -z "$cmd" ]] && continue | |
| adb_cmd "$cmd" >/dev/null 2>&1 || true | |
| done < "$restore_script" | |
| done | |
| ok "Przywracanie zakończone." | |
| pause | |
| } | |
| list_backups() { | |
| header "ISTNIEJĄCE BACKUPY" | |
| find "$LOG_DIR" -name "*.sh" -o -name "*.txt" 2>/dev/null | while IFS= read -r f; do | |
| printf " ${CYAN}%s${RESET} (%.1f KB)\n" "$f" "$(du -k "$f" | cut -f1)" | |
| done || info "Brak backupów." | |
| pause | |
| } | |
| export_backup_to_watch() { | |
| step "Eksportuje backup na zegarek..." | |
| adb_cmd "mkdir -p /sdcard/watch4_backup/" >/dev/null 2>&1 | |
| for f in "${BACKUP_DIR}"/*.sh "${BACKUP_DIR}"/*.txt; do | |
| [[ -f "$f" ]] || continue | |
| adb push "$f" "/sdcard/watch4_backup/$(basename "$f")" >/dev/null 2>&1 && ok "$(basename "$f")" || warn "Nie można przesłać: $f" | |
| done | |
| ok "Backup dostępny na zegarku: /sdcard/watch4_backup/" | |
| pause | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # SEKCJA 10 — MENU GŁÓWNE | |
| # ═══════════════════════════════════════════════════════════════════ | |
| main_menu() { | |
| while true; do | |
| print_banner | |
| printf " ${BOLD}Urządzenie: ${CYAN}%s${RESET} | ${BOLD}Model: ${CYAN}%s${RESET} | ${BOLD}Android: ${CYAN}%s${RESET}\n\n" \ | |
| "$DEVICE_SERIAL" "$DEVICE_MODEL" "$DEVICE_ANDROID" | |
| echo -e " ${BOLD}${WHITE}[ DIAGNOSTYKA ]${RESET}" | |
| echo " [1] Diagnostyka proaktywna (monitoring, logi, analiza)" | |
| echo | |
| echo -e " ${BOLD}${WHITE}[ OPTYMALIZACJA ]${RESET}" | |
| echo " [2] Zarządzanie AOD (Always-On Display)" | |
| echo " [3] Wydajność systemu (animacje, GPU, ART, CPU)" | |
| echo | |
| echo -e " ${BOLD}${WHITE}[ ZARZĄDZANIE SYSTEMEM ]${RESET}" | |
| echo " [4] Debloat — bezpieczne zarządzanie aplikacjami" | |
| echo " [5] Naprawa systemu (UI, sieć, sensory, restart)" | |
| echo | |
| echo -e " ${BOLD}${WHITE}[ DANE ]${RESET}" | |
| echo " [6] Backup i przywracanie ustawień" | |
| echo | |
| echo -e " ${BOLD}${WHITE}[ SZYBKIE AKCJE ]${RESET}" | |
| echo " [T] ⚡ Profil TURBO — pełna optymalizacja" | |
| echo " [R] ↩ Przywróć domyślne ustawienia wydajności" | |
| echo " [Q] Rozłącz i zakończ" | |
| sep | |
| read -rp "$(printf " Wybór: ")" choice | |
| case "${choice,,}" in | |
| 1) diagnostics_menu ;; | |
| 2) aod_menu ;; | |
| 3) performance_menu ;; | |
| 4) debloat_menu ;; | |
| 5) repair_menu ;; | |
| 6) backup_menu ;; | |
| t) apply_turbo_profile ;; | |
| r) restore_defaults_perf ;; | |
| q) | |
| info "Rozłączam ${DEVICE_SERIAL}..." | |
| adb disconnect "$DEVICE_SERIAL" &>/dev/null || true | |
| ok "Sesja zakończona." | |
| info "Log sesji: ${LOG_FILE}" | |
| if [[ "$BACKUP_DONE" == "true" ]]; then | |
| info "Backup ustawień: ${BACKUP_DIR}/" | |
| fi | |
| exit 0 | |
| ;; | |
| *) warn "Nieprawidłowy wybór." ; sleep 1 ;; | |
| esac | |
| done | |
| } | |
| # ═══════════════════════════════════════════════════════════════════ | |
| # PUNKT WEJŚCIA | |
| # ═══════════════════════════════════════════════════════════════════ | |
| main() { | |
| print_banner | |
| log "=== ${SCRIPT_NAME} v${VERSION} — Sesja rozpoczęta ===" | |
| log "System: $(uname -a)" | |
| check_adb_installed | |
| connect_device | |
| main_menu | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment