Skip to content

Instantly share code, notes, and snippets.

@anonymousik
Created February 24, 2026 05:53
Show Gist options
  • Select an option

  • Save anonymousik/9246ecfbc8ec61a673ef249fa631c275 to your computer and use it in GitHub Desktop.

Select an option

Save anonymousik/9246ecfbc8ec61a673ef249fa631c275 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
╔══════════════════════════════════════════════════════════════════════════════╗
║ PLAYBOX TITANIUM v11.0 ULTRA — SmartTube + Chromecast EDITION ║
║ Target : Sagemcom DCTIW362P | Android TV 9 (API 28) | Broadcom m362 ║
║ Focus : Max SmartTube/Video Performance + Chromecast Service Protection ║
║ Author : Anonymousik / SecFerro Division ║
║ Date : 2026-02 ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ HARDWARE PROFILE (real ADB session, 192.168.1.3:5555): ║
║ RAM : 1459 MB total | ~534 MB available (MemAvailable) ║
║ FLASH : 3.9 GB | 53% used (/data) ║
║ SoC : Broadcom m362 (BCM7362) — Dual-core Cortex-A15 @ 1.0 GHz ║
║ VPU : BCM7362 HW decoder: H.264 ✓ | H.265 ✓ | VP9 ✓ | AV1 ✗ ║
║ GPU : Broadcom VideoCore (OpenGL ES 3.1, NO Vulkan) ║
║ Display : HDMI 2.0a, CEC v1.4, 4K@60fps, HDR10 ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ CHROMECAST PROTECTION ARCHITECTURE: ║
║ apps.mediashell → Cast Built-in DAEMON [NEVER DISABLE] ║
║ gms → Cast SDK / Auth [NEVER DISABLE] ║
║ gsf → Google Services FW [NEVER DISABLE] ║
║ nearby → mDNS/Cast Discovery [NEVER DISABLE] ║
║ tv.remote.service → Cast UI Input [PROTECTED] ║
║ tvlauncher → Cast Session UI [PROTECTED] ║
║ networkstack → Multicast Routing [PROTECTED] ║
╚══════════════════════════════════════════════════════════════════════════════╝
"""
from __future__ import annotations
import os
import sys
import subprocess
import time
import argparse
from pathlib import Path
from typing import Optional, List, Dict, Tuple, Callable
# ─────────────────────────────────────────────────────────────────────────────
# GLOBAL CONSTANTS
# ─────────────────────────────────────────────────────────────────────────────
VERSION = "11.0-ULTRA-SmartTube-ChromecastSafe"
DEFAULT_ADB_HOST = "192.168.1.3:5555"
CACHE_DIR = Path.home() / ".playbox_cache"
BACKUP_DIR = CACHE_DIR / "backups_v11"
LOG_FILE = CACHE_DIR / "autopilot_v11.log"
CACHE_DIR.mkdir(parents=True, exist_ok=True)
BACKUP_DIR.mkdir(parents=True, exist_ok=True)
# ─────────────────────────────────────────────────────────────────────────────
# DEVICE SPECIFICATION — Broadcom BCM7362
# ─────────────────────────────────────────────────────────────────────────────
class DeviceSpec:
"""
Hardware constants for Sagemcom DCTIW362P.
Sources: real ADB capture + BCM7362 datasheet + Android TV 9 framework docs.
"""
MODEL = "dctiw362p"
HARDWARE = "m362"
ANDROID_VERSION = "9"
API_LEVEL = "28"
BUILD_FINGERPRINT = (
"PLAY/play_dctiw362/dctiw362p:9/PTT1.190826.001/"
"1.0.36-194202:user/release-keys"
)
# From /proc/meminfo snapshot
RAM_TOTAL_KB = 1_459_656
RAM_AVAILABLE_KB = 534_720
# BCM7362 VPU codec identifiers (OMX layer)
# Confirmed by OMX component enumeration on this chipset family
CODEC_HW_H264 = "OMX.brcm.video.h264.decoder"
CODEC_HW_HEVC = "OMX.brcm.video.h265.decoder"
CODEC_HW_VP9 = "OMX.brcm.video.vp9.decoder"
# AV1: NO hardware decoder on BCM7362 — software fallback only
CODEC_SW_AV1 = "c2.android.av1.decoder" # DO NOT USE — CPU killer
# Dalvik heap profile for ~534MB available RAM
# SmartTube typical heap usage: 150–200MB during 4K playback
DALVIK_HEAP_SIZE = "256m"
DALVIK_HEAP_GROWTH = "128m"
DALVIK_HEAP_MIN_FREE = "16m"
DALVIK_HEAP_MAX_FREE = "32m"
DALVIK_HEAP_TARGET = "0.75"
SMARTTUBE_PKG = "com.teamsmart.videomanager.tv"
SMARTTUBE_STABLE_URL = (
"https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_stable.apk"
)
SMARTTUBE_BETA_URL = (
"https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_beta.apk"
)
SHIZUKU_PKG = "moe.shizuku.privileged.api"
SHIZUKU_APK_URL = (
"https://github.com/RikkaApps/Shizuku/releases/download/"
"v13.5.4/shizuku-v13.5.4-release.apk"
)
# ─────────────────────────────────────────────────────────────────────────────
# CHROMECAST PROTECTION REGISTRY
# ─────────────────────────────────────────────────────────────────────────────
class ChromecastProtection:
"""
Definitive whitelist of packages that MUST remain enabled for
Chromecast Built-in, Google Cast SDK v3+, and related protocols.
Engineering rationale per entry:
──────────────────────────────────────────────────────────────────────────
apps.mediashell → THIS IS Chromecast Built-in daemon on Android TV.
Without it, no Cast session can be established.
It runs the Cast receiver application loop and
manages DIAL/mDNS session handshake.
gms → Google Play Services. Hosts:
· com.google.android.gms.cast (Cast SDK)
· SessionManager, CastContext
· Device authentication (OAuth tokens)
Disabling GMS = total Cast failure.
gsf → Google Services Framework. Core dependency of GMS.
Handles Google Account token refresh. Without it,
GMS cannot authenticate → Cast breaks silently.
nearby → Google Nearby Connections. Manages:
· mDNS/DNS-SD multicast (port 5353)
Used for Chromecast device discovery on LAN
· BLE advertising (Guest Mode / Fast Pair)
· SSDP/UPnP fallback discovery path
Disabling = sender device "cannot find Chromecast".
tv.remote.service → Handles Cast session initiation events from remote
controls and the "Cast to TV" system intent. Also
manages virtual remote input during active sessions.
tvlauncher → Android TV Home. Cast sessions surface ambient
mode UI here. Handles android.intent.action.MAIN
with CATEGORY_LEANBACK_LAUNCHER for Cast handoff.
configupdater → Downloads periodic Cast infrastructure config:
TLS certificate pins, endpoint URLs, protocol
version negotiation. Stale config → Cast auth fails.
wifidisplay → Miracast / WiFi Direct. Used as Cast fallback
transport when DIAL-over-mDNS fails (e.g. IGMP
snooping misconfigured on router). Some Chromecast
Audio receivers rely on this path.
networkstack → Android network stack. Handles multicast group
join (IGMP) for mDNS. Without it, mDNS packets
are never delivered to the mDNS responder daemon.
networkstack.tether → Tethering companion — shares multicast routing
rules with networkstack. Required on some builds
for proper multicast socket binding.
"""
PROTECTED_PACKAGES: Dict[str, str] = {
# Cast Core — NEVER touch these
"com.google.android.apps.mediashell":
"Cast Built-in DAEMON — the Chromecast receiver process [CRITICAL]",
"com.google.android.gms":
"Google Play Services — Cast SDK, SessionManager, Auth [CRITICAL]",
"com.google.android.gsf":
"Google Services Framework — GMS dependency chain [CRITICAL]",
"com.google.android.nearby":
"Nearby Connections — mDNS/BLE Chromecast discovery [CRITICAL]",
# Cast Session Management
"com.google.android.tv.remote.service":
"TV Remote Service — Cast session UI & input routing [IMPORTANT]",
"com.google.android.tvlauncher":
"TV Launcher — Cast ambient mode UI & intents [IMPORTANT]",
# Cast Infrastructure
"com.google.android.configupdater":
"Config Updater — Cast TLS certs & endpoint config [RECOMMENDED]",
"com.google.android.wifidisplay":
"WiFi Display — Miracast/Cast fallback transport [RECOMMENDED]",
# Network Foundation (multicast for mDNS)
"com.android.networkstack":
"Network Stack — IGMP multicast for mDNS (Cast discovery) [CRITICAL]",
"com.android.networkstack.tethering":
"Network Tethering Stack — multicast socket routing [IMPORTANT]",
}
@classmethod
def is_protected(cls, package: str) -> bool:
return package in cls.PROTECTED_PACKAGES
@classmethod
def get_reason(cls, package: str) -> str:
return cls.PROTECTED_PACKAGES.get(package, "")
@classmethod
def print_registry(cls) -> None:
Logger.header("🛡 CHROMECAST PROTECTION REGISTRY")
for pkg, reason in cls.PROTECTED_PACKAGES.items():
Logger.log(f" 🔒 {pkg}", "success")
Logger.log(f" └─ {reason}", "cyan")
Logger.log(
f"\n Total protected: {len(cls.PROTECTED_PACKAGES)} packages",
"bold"
)
# ─────────────────────────────────────────────────────────────────────────────
# SAFE DEBLOAT DATABASE
# Each entry: (package, reason_to_disable, chromecast_impact)
# ─────────────────────────────────────────────────────────────────────────────
SAFE_DEBLOAT_DB: List[Tuple[str, str, str]] = [
# Android TV system bloat
("com.google.android.backdrop",
"Ambient Mode screensaver — idles GPU, wastes 30–60MB RAM",
"NONE — Cast unaffected"),
("com.google.android.tvrecommendations",
"Content recommendations — constant background HTTP polling",
"NONE"),
("com.google.android.katniss",
"Google TV voice search overlay — high idle CPU on A15",
"NONE — Cast uses own discovery path"),
("com.google.android.tungsten.setupwraith",
"Setup wizard — not needed post-initial-setup",
"NONE"),
("com.google.android.apps.tv.launcherx",
"New Google TV launcher (not used on API 28 / Android 9)",
"NONE — tvlauncher separately protected"),
("com.google.android.leanbacklauncher",
"Legacy Leanback launcher duplicate (tvlauncher supersedes it)",
"NONE"),
# Sagemcom / Orange Poland bloat
("com.sagemcom.stb.setupwizard",
"Sagemcom factory setup wizard",
"NONE"),
("com.orange.fr.tv.assistance",
"Orange TV assistance app — polls Orange servers constantly",
"NONE"),
("com.orange.fr.tv.guide",
"Orange TV guide — heavy background sync, rarely used",
"NONE"),
# Google services safe to disable (non-Cast)
("com.google.android.marvin.talkback",
"Accessibility TTS — not needed on TV, allocates ~40MB",
"NONE"),
("com.google.android.onetimeinitializer",
"One-time initializer — already completed on first boot",
"NONE"),
# Third-party pre-installed
("com.amazon.amazonvideo.livingroom",
"Amazon Prime TV — pre-bundled, standalone install preferable",
"NONE"),
]
# ─────────────────────────────────────────────────────────────────────────────
# LOGGER
# ─────────────────────────────────────────────────────────────────────────────
class Logger:
COLORS: Dict[str, str] = {
"info": "\033[94m",
"success": "\033[92m",
"warning": "\033[93m",
"error": "\033[91m",
"header": "\033[95m",
"cyan": "\033[96m",
"bold": "\033[1m",
"reset": "\033[0m",
}
_log_lines: List[str] = []
@classmethod
def _emit(cls, msg: str, level: str) -> None:
ts = time.strftime("%H:%M:%S")
color = cls.COLORS.get(level, cls.COLORS["info"])
print(f"{color}[{ts}] {msg}{cls.COLORS['reset']}")
cls._log_lines.append(f"[{ts}][{level.upper()}] {msg}")
@classmethod
def log(cls, msg: str, level: str = "info") -> None:
cls._emit(msg, level)
@classmethod
def success(cls, msg: str) -> None:
cls._emit(f"✓ {msg}", "success")
@classmethod
def warning(cls, msg: str) -> None:
cls._emit(f"⚠ {msg}", "warning")
@classmethod
def error(cls, msg: str) -> None:
cls._emit(f"✗ {msg}", "error")
@classmethod
def shield(cls, msg: str) -> None:
"""Protected resource notification."""
cls._emit(f"🛡 {msg}", "success")
@classmethod
def header(cls, msg: str) -> None:
sep = "═" * 74
print(
f"\n{cls.COLORS['header']}{cls.COLORS['bold']}{sep}\n"
f" {msg}\n{sep}{cls.COLORS['reset']}\n"
)
@classmethod
def save_log(cls) -> None:
try:
with open(LOG_FILE, "a", encoding="utf-8") as fh:
fh.write(f"\n{'─'*60}\n")
fh.write(f"SESSION {time.strftime('%Y-%m-%d %H:%M:%S')} | v{VERSION}\n")
fh.write("\n".join(cls._log_lines))
fh.write("\n")
except OSError:
pass
# ─────────────────────────────────────────────────────────────────────────────
# ADB SHELL INTERFACE
# ─────────────────────────────────────────────────────────────────────────────
class ADBShell:
"""
Secure, idempotent ADB command executor.
Security notes:
- No shell=True — commands passed as list to prevent injection
- Retry only on TimeoutExpired, not CalledProcessError (errors are final)
- run_root() escalation: su → rish (Shizuku) → plain ADB shell
"""
device: Optional[str] = None
TIMEOUT: int = 30
RETRIES: int = 3
@classmethod
def connect(cls, target: str) -> bool:
"""Establish TCP ADB connection. Returns True on success."""
try:
result = subprocess.run(
["adb", "connect", target],
capture_output=True, text=True, timeout=10
)
if "connected" in result.stdout.lower():
cls.device = target
Logger.success(f"ADB connected: {target}")
return True
Logger.error(f"ADB connect failed: {result.stdout.strip()}")
return False
except FileNotFoundError:
Logger.error("'adb' not found in PATH. Install Android Platform Tools.")
sys.exit(1)
except subprocess.TimeoutExpired:
Logger.error(f"ADB connect timeout: {target}")
return False
@classmethod
def detect_device(cls) -> Optional[str]:
"""Auto-detect first available adb device."""
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 run(
cls,
cmd: str,
ignore_errors: bool = True,
silent: bool = False,
) -> str:
"""Execute shell command via ADB. Returns stdout or '' on failure."""
if not cls.device:
return ""
for attempt in range(cls.RETRIES):
try:
out = subprocess.check_output(
["adb", "-s", cls.device, "shell", cmd],
stderr=subprocess.STDOUT,
text=True,
timeout=cls.TIMEOUT,
)
return out.strip()
except subprocess.TimeoutExpired:
if attempt < cls.RETRIES - 1:
time.sleep(1.5)
elif not silent:
Logger.warning(f"Timeout [{attempt+1}/{cls.RETRIES}]: {cmd[:60]}")
except subprocess.CalledProcessError as exc:
return exc.output.strip() if exc.output else ""
except Exception as exc:
if not silent:
Logger.error(f"ADB exception: {exc}")
return ""
return ""
@classmethod
def run_root(cls, cmd: str) -> str:
"""Privileged execution: su → rish → plain ADB shell (graceful fallback)."""
for prefix in (f"su -c \"{cmd}\"", f"rish -c \"{cmd}\""):
result = cls.run(prefix, ignore_errors=True, silent=True)
if result and "not found" not in result and "permission denied" not in result.lower():
return result
Logger.warning(f"Root unavailable, trying plain ADB: {cmd[:50]}")
return cls.run(cmd, ignore_errors=True)
@classmethod
def push(cls, local: str, remote: str) -> bool:
"""Push file to device. Returns True on success."""
try:
subprocess.check_call(
["adb", "-s", cls.device, "push", local, remote],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
timeout=120,
)
return True
except Exception:
return False
# ── Convenience wrappers ──────────────────────────────────────────────
@classmethod
def prop_get(cls, prop: str) -> str:
return cls.run(f"getprop {prop}", silent=True)
@classmethod
def prop_set(cls, prop: str, value: str) -> None:
cls.run(f"setprop {prop} {value}", ignore_errors=True, silent=True)
@classmethod
def setting_put(cls, ns: str, key: str, value: str) -> None:
cls.run(f"settings put {ns} {key} {value}", ignore_errors=True, silent=True)
@classmethod
def setting_get(cls, ns: str, key: str) -> str:
return cls.run(f"settings get {ns} {key}", silent=True)
@classmethod
def pkg_enabled(cls, pkg: str) -> bool:
return pkg in cls.run(f"pm list packages -e {pkg}", silent=True)
# ─────────────────────────────────────────────────────────────────────────────
# SMARTTUBE VIDEO ENGINE OPTIMIZER
# ─────────────────────────────────────────────────────────────────────────────
class SmartTubeOptimizer:
"""
SmartTube + ExoPlayer + Broadcom VPU pipeline tuning.
SmartTube uses ExoPlayer which interfaces with Android MediaCodec API:
MediaCodec → OMX layer → BCM7362 VPU kernel driver (bcmvideo.ko)
YouTube codec priority for BCM7362 (HW → SW, best → worst):
1. VP9 profile 2 — HW decode, 4K@60fps, best quality/perf ratio
2. H.264 AVC — HW decode, universal compatibility
3. H.265 HEVC — HW decode, good for 4K, less stable on some sources
4. AV1 — SOFTWARE ONLY → CPU at 100%, dropped frames, throttle
Tunnel Mode (Android 5+, BCM7362 supported):
Decoded VPU frames → display controller, bypassing SurfaceFlinger.
Eliminates 1–2 frame compositing latency, saves ~15% CPU at 4K.
"""
def optimize_exoplayer_pipeline(self) -> None:
Logger.header("🎬 EXOPLAYER PIPELINE — Broadcom VPU Hardware Path")
# Stagefright / MediaCodec stack flags
codec_props = [
# Enable HW-accelerated player pipeline
("media.stagefright.enable-player", "true"),
("media.stagefright.enable-meta", "true"),
("media.stagefright.enable-http", "true"),
("media.stagefright.enable-scan", "true"),
# OMX: always pick HW codec over SW fallback during negotiation
("media.acodec.preferhw", "true"),
("media.vcodec.preferhw", "true"),
# Suppress automatic SW fallback for HW-capable codecs
("media.codec.sw.fallback", "false"),
# OMX thread priority: 1 = RT class (prevents UI thread preemption)
("media.codec.priority", "1"),
]
for key, value in codec_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
# HLS / DASH adaptive bitrate buffer tuning
abr_props = [
# 25 Mbps ceiling covers YouTube 4K VP9 (typical: 10–20 Mbps)
("media.httplive.max-bitrate", "25000000"),
# Start at 8 Mbps — avoids quality dip on stream open
("media.httplive.initial-bitrate", "8000000"),
# 60s lookahead buffer — reduces rebuffering events on variable WiFi
("media.httplive.max-live-offset", "60"),
# Bandwidth estimator update interval: 1000ms (responsive ABR)
("media.httplive.bw-update-interval", "1000"),
]
for key, value in abr_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
# MediaExtractor cache: 20MB min / 60MB max / 256MB total cap
# Calculated for 4K streaming on 1.4GB RAM device (holds ~3s of 4K)
ADBShell.run(
"settings put global media.stagefright.cache-params "
"20971520/62914560/268435456",
ignore_errors=True,
)
Logger.log(" ✓ MediaExtractor cache: 20MB–60MB / 256MB cap")
Logger.success("ExoPlayer pipeline configured for Broadcom VPU HW path")
def force_tunnel_mode(self) -> None:
"""
Enable MediaCodec Tunnel Mode.
VPU decoded frames → BCM7362 display controller directly,
bypassing SurfaceFlinger buffer queue. Effect:
- ~1–2 frame latency reduction
- ~15% CPU reduction during 4K playback
- Eliminates compositor-induced frame pacing jitter
"""
Logger.header("🚇 TUNNEL MODE — Direct VPU→Display Path")
tunnel_props = [
("media.tunneled-playback.enable", "true"),
("media.brcm.tunnel.sessions", "1"), # One tunnel = one video
("media.brcm.hdmi.tunnel", "true"),
("media.brcm.tunnel.clock", "hdmi"), # Lock A/V to HDMI sink clock
("debug.sf.disable_backpressure", "1"), # No SF back-pressure on VPU
]
for key, value in tunnel_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
Logger.success("Tunnel Mode active — VPU→Display direct path enabled")
def suppress_av1_software_decode(self) -> None:
"""
Suppress AV1 software decoder to prevent CPU overload.
BCM7362 has NO AV1 hardware decoder. YouTube increasingly serves
AV1 to devices that advertise VP9 + AV1 support. ExoPlayer then
selects c2.android.av1.decoder — a pure software decoder that
saturates both A15 cores at 720p, causing frame drops at 1080p+.
We suppress the component so MediaCodec negotiation falls back to
VP9 (hardware), which BCM7362 handles at 4K@60fps without CPU load.
"""
Logger.header("🚫 AV1 SUPPRESSION — Force VP9/H.264 HW Negotiation")
Logger.warning("BCM7362: NO AV1 HW decoder — SW decode = CPU 100% + stutter")
av1_props = [
("debug.stagefright.c2.av1", "0"),
("media.av1.sw.decode.disable", "true"),
("media.codec.av1.disable", "true"),
]
for key, value in av1_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
Logger.success("AV1 SW decoder suppressed — VP9/H.264 HW path will be used")
colors = Logger.COLORS
print(f"""
{colors['cyan']}┌──────────────────────────────────────────────────────────┐
│ ⚙ SMARTTUBE MANUAL SETTINGS (after next launch) │
│ │
│ Settings → Player → Video codec → VP9 ← set this │
│ Settings → Player → Resolution → 2160p (4K) │
│ Settings → Player → Use tunnel mode → ON (if visible) │
│ │
│ VP9 on BCM7362 = hardware → smooth 4K, low CPU │
│ AV1 on BCM7362 = software → stutter, thermal throttle │
└──────────────────────────────────────────────────────────┘{colors['reset']}
""")
def tune_dalvik_heap(self) -> None:
"""
Tune Dalvik/ART heap for streaming workload on 1.4GB RAM device.
Memory budget (MemAvailable ~534MB):
SmartTube (playing 4K) : ~200MB heap + ~50MB native
Chromecast GMS/mediashell: ~80MB
TV Launcher : ~40MB
OS + kernel : ~150MB
Headroom : ~14MB free
→ heap_size=256m keeps SmartTube happy without GC storms
"""
Logger.header("🧠 DALVIK/ART HEAP — SmartTube 4K Streaming Profile")
heap_props = [
("dalvik.vm.heapsize", DeviceSpec.DALVIK_HEAP_SIZE),
("dalvik.vm.heapgrowthlimit", DeviceSpec.DALVIK_HEAP_GROWTH),
("dalvik.vm.heapminfree", DeviceSpec.DALVIK_HEAP_MIN_FREE),
("dalvik.vm.heapmaxfree", DeviceSpec.DALVIK_HEAP_MAX_FREE),
("dalvik.vm.heaptargetutilization", DeviceSpec.DALVIK_HEAP_TARGET),
("dalvik.vm.usejit", "true"), # JIT: reduce first-run latency
("dalvik.vm.dex2oat-filter", "speed-profile"),
("dalvik.vm.gctype", "CMS"), # Concurrent GC → no pause
]
for key, value in heap_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
Logger.success("Dalvik heap configured for SmartTube 4K workload")
def compile_aot(self) -> bool:
"""
AOT-compile SmartTube to eliminate JIT compilation bursts during playback.
JIT compilation on A15 cores causes 50–200ms spikes that manifest as
dropped frames during seeks or first few seconds of playback.
AOT pre-compiles hot paths (player, network, UI) → smooth from first frame.
Duration: ~60–90s on BCM7362 (dual-core 1.0 GHz A15). Persistent until APK update.
"""
Logger.header("⚡ AOT COMPILATION — Eliminate JIT Bursts in SmartTube")
pkg = DeviceSpec.SMARTTUBE_PKG
if not ADBShell.pkg_enabled(pkg):
Logger.warning(f"SmartTube not installed — skipping AOT")
return False
Logger.log("Compiling SmartTube [speed-profile mode] ... ~60–90s")
result = ADBShell.run(
f"cmd package compile -m speed-profile -f {pkg}",
ignore_errors=True,
)
if "success" in result.lower():
Logger.success("SmartTube AOT complete (speed-profile)")
return True
Logger.warning("speed-profile failed — retrying with speed mode")
result = ADBShell.run(f"cmd package compile -m speed -f {pkg}", ignore_errors=True)
Logger.success("SmartTube AOT complete (speed mode)")
return True
# ─────────────────────────────────────────────────────────────────────────────
# CHROMECAST SERVICE MANAGER
# ─────────────────────────────────────────────────────────────────────────────
class ChromecastServiceManager:
"""
Audit, restore, and harden all Chromecast Built-in services.
Cast discovery call stack (simplified):
Sender app → mDNS query (UDP 5353 multicast 224.0.0.251)
→ com.android.networkstack (multicast join / IGMP)
→ com.google.android.nearby (mDNS responder)
→ com.google.android.apps.mediashell (Cast session accept)
→ com.google.android.gms (Cast SDK lifecycle)
→ com.google.android.tvlauncher (Cast UI)
"""
@staticmethod
def audit() -> Dict[str, bool]:
"""Check enabled state of all protected Cast packages."""
Logger.header("🔍 CHROMECAST SERVICE AUDIT")
results: Dict[str, bool] = {}
for pkg, reason in ChromecastProtection.PROTECTED_PACKAGES.items():
enabled = ADBShell.pkg_enabled(pkg)
results[pkg] = enabled
icon = "✓" if enabled else "✗"
level = "success" if enabled else "error"
Logger.log(f" {icon} {pkg}", level)
Logger.log(f" └─ {reason}", "cyan")
broken = [p for p, e in results.items() if not e]
if broken:
Logger.warning(
f"{len(broken)} Cast service(s) DISABLED — Chromecast may be broken!"
)
else:
Logger.success("All Chromecast services active and healthy ✓")
return results
@staticmethod
def restore() -> None:
"""Re-enable all Cast services that are currently disabled. Idempotent."""
Logger.header("🛡 CHROMECAST SERVICE RESTORATION")
for pkg, reason in ChromecastProtection.PROTECTED_PACKAGES.items():
ADBShell.run(f"pm enable {pkg}", ignore_errors=True, silent=True)
ADBShell.run(f"pm enable --user 0 {pkg}", ignore_errors=True, silent=True)
Logger.shield(f"Ensured enabled: {pkg}")
Logger.success(
f"Restoration complete — {len(ChromecastProtection.PROTECTED_PACKAGES)} "
"Cast services verified"
)
@staticmethod
def optimize_cast_network() -> None:
"""
Optimize WiFi/multicast for reliable Cast discovery.
Common issue: WiFi driver power-save mode suppresses multicast packets,
causing intermittent 'Chromecast not found' on sender devices even when
device is online (unicast works, multicast 224.0.0.251 dropped).
"""
Logger.header("📡 CAST NETWORK — mDNS/Multicast Reliability")
# Keep WiFi alive to prevent multicast suppression
ADBShell.setting_put("global", "wifi_sleep_policy", "2")
Logger.log(" ✓ wifi_sleep_policy = 2 (always connected)")
ADBShell.setting_put("global", "wifi_power_save", "0")
Logger.log(" ✓ wifi_power_save disabled (prevents mDNS packet drop)")
# mDNS passive mode off → device actively responds to discovery
ADBShell.prop_set("ro.mdns.enable_passive_mode", "false")
Logger.log(" ✓ mDNS: active response mode")
# SSDP multicast TTL ≥ 1 required for LAN multicast delivery
ADBShell.prop_set("net.ssdp.ttl", "4")
Logger.log(" ✓ SSDP TTL = 4 (sufficient for home LAN)")
Logger.success("Cast mDNS/multicast network profile active")
@staticmethod
def harden_cast_processes() -> None:
"""
Set OOM score for Cast processes to prevent LMK from killing them.
oom_score_adj scale:
-1000 = never kill (system)
0 = foreground app
200 = empty background (kill first)
We set Cast daemon to ~100 → "important background service" tier
"""
Logger.header("🎯 CAST PROCESS HARDENING — OOM Protection")
cast_pkgs = [
"com.google.android.apps.mediashell",
"com.google.android.gms",
"com.google.android.nearby",
]
for pkg in cast_pkgs:
pid = ADBShell.run(f"pidof {pkg}", silent=True).strip()
if pid and pid.isdigit():
ADBShell.run_root(f"echo 100 > /proc/{pid}/oom_score_adj")
Logger.shield(f"OOM adj=100 set: {pkg} (PID {pid})")
else:
Logger.log(f" {pkg} not running — will be protected at runtime", "warning")
Logger.success("Cast processes protected against OOM killer")
# ─────────────────────────────────────────────────────────────────────────────
# BROADCOM M362 GPU + VPU TUNER
# ─────────────────────────────────────────────────────────────────────────────
class BroadcomTuner:
"""
Hardware-specific tuning for Broadcom BCM7362 (m362).
Architecture:
- Dual-core Cortex-A15 @ 1.0 GHz (NOT A53 — this is an STB SoC)
- VideoCore VI GPU (GLES 3.1 max, NO Vulkan, NO Swiftshader)
- Dedicated VPU silicon — H.264/H.265/VP9 decode completely offloaded
- Triple-buffer framebuffer in hardware (prevents screen tearing)
- HDMI 2.0a output with native HDR10 signaling
"""
def apply_gpu_tweaks(self) -> None:
Logger.header("🎮 BROADCOM VIDEOCORE — GPU Rendering Optimization")
gpu_props = [
("debug.hwui.renderer", "opengl"), # Must be opengl — no Vulkan
("debug.egl.hw", "1"), # HW EGL acceleration
("debug.sf.hw", "1"), # SurfaceFlinger HW composer
("debug.gr.numframebuffers", "3"), # Triple buffer (tearing fix)
("debug.hwui.use_buffer_age", "false"), # Unnecessary overhead on VideoCore
("debug.hwui.layer_cache_size", "16384"), # 16MB layer cache
("debug.hwui.profile", "false"), # No profiler overhead
]
for key, value in gpu_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
ADBShell.setting_put("global", "force_gpu_rendering", "true")
Logger.log(" ✓ force_gpu_rendering = true")
Logger.success("VideoCore GPU tweaks applied")
def apply_vpu_tweaks(self) -> None:
Logger.header("📹 BROADCOM VPU — Video Decode Pipeline")
vpu_props = [
("ro.hdmi.device_type", "4"), # Playback device
("persist.sys.hdmi.addr.playback", "0"),
("ro.hdmi.wake_on_hotplug", "false"), # Prevent HDMI reset glitches
("persist.sys.hdr.enable", "1"), # HDR10 output
("media.brcm.vpu.prealloc", "true"), # Pre-alloc decode buffers at boot
("media.brcm.vpu.buffers", "6"), # 6 frame buffer pool (4K@60)
("media.brcm.secure.decode", "true"), # Widevine L3 / PlayReady support
]
for key, value in vpu_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
Logger.success("BCM7362 VPU pipeline configured")
def set_performance_governor(self) -> None:
"""
Force 'performance' CPU governor on both A15 cores.
Under 'interactive' governor, cores can drop to 250MHz, causing
decode queue starvation and visible frame pacing issues.
"""
Logger.header("⚙ CPU GOVERNOR — Force Performance Mode")
for core in range(2): # BCM7362: 2x Cortex-A15
path = f"/sys/devices/system/cpu/cpu{core}/cpufreq/scaling_governor"
result = ADBShell.run_root(f"echo performance > {path}")
Logger.log(f" ✓ cpu{core}: performance governor")
Logger.success("Both A15 cores locked to maximum frequency")
# ─────────────────────────────────────────────────────────────────────────────
# AUDIO PIPELINE OPTIMIZER
# ─────────────────────────────────────────────────────────────────────────────
class AudioOptimizer:
"""
Fix A/V desync and optimize audio output for Orange PLAYBox.
Root cause of desync on BCM7362 HDMI:
Audio offload path uses Broadcom's proprietary timing that disagrees
with HDMI ARC handshake timing. Result: audio runs 50–200ms off from video.
Fix: disable offload → passthrough mode → consistent latency.
Lock audio clock to HDMI sink → eliminates slow drift over hours.
"""
def fix_av_sync_and_pipeline(self) -> None:
Logger.header("🔊 AUDIO PIPELINE — A/V Sync + HDMI Fix")
audio_props = [
# Disable audio offload — it causes desync on BCM7362 HDMI output
("audio.offload.disable", "1"),
("audio.offload.video", "0"),
# Disable tunnel audio encode (conflicts with HDMI ARC on this chipset)
("tunnel.audio.encode", "false"),
# Deep buffer: stable ~20ms latency, prevents buffer under-runs
("audio.deep_buffer.media", "true"),
# One fast track sufficient for SmartTube (mono audio thread)
("af.fast_track_multiplier", "1"),
# Lock audio clock to HDMI sink → eliminates slow A/V drift
("audio.brcm.hdmi.clock_lock", "true"),
# HAL latency: 20ms (optimal for BCM7362)
("audio.brcm.hal.latency", "20"),
]
for key, value in audio_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
Logger.success("Audio pipeline fixed — A/V sync stabilized, HDMI clock locked")
# ─────────────────────────────────────────────────────────────────────────────
# NETWORK OPTIMIZER
# ─────────────────────────────────────────────────────────────────────────────
class NetworkOptimizer:
"""
TCP/IP stack + DNS tuning for 4K streaming.
Buffer sizing rationale (RTT ~5ms LAN, 100Mbps link):
BDP = 100Mbps × 5ms = 62.5 KB
We provision 4× BDP for burst absorption: ~250KB receive, ~512KB send
(4K VP9 YouTube stream: 10–20Mbps → burst to 25Mbps for ~100ms)
Cast-safe: multicast settings untouched. Private DNS (DoT) only
affects unicast DNS — mDNS uses multicast UDP, completely separate path.
"""
DNS_PROVIDERS: Dict[str, str] = {
"cloudflare": "one.one.one.one",
"google": "dns.google",
"nextdns": "dns.nextdns.io",
"quad9": "dns.quad9.net",
"adguard": "dns.adguard.com",
}
def apply_tcp_tuning(self) -> None:
Logger.header("🌐 NETWORK — TCP Stack 4K Streaming Profile")
Logger.shield(
"mDNS/multicast settings preserved — Cast discovery unaffected by TCP tuning"
)
# TCP buffer sizes: min/default/max (bytes)
ADBShell.setting_put(
"global", "net.tcp.buffersize.wifi",
"262144,1048576,2097152,131072,524288,1048576"
)
Logger.log(" ✓ WiFi TCP buffers: 256KB / 1MB / 2MB")
ADBShell.setting_put(
"global", "net.tcp.buffersize.ethernet",
"524288,2097152,4194304,262144,1048576,2097152"
)
Logger.log(" ✓ Ethernet TCP buffers: 512KB / 2MB / 4MB")
# Initial receive window: 60 segments = ~87KB (fast stream start)
ADBShell.setting_put("global", "tcp_default_init_rwnd", "60")
Logger.log(" ✓ TCP initial receive window: 60 segments (~87KB)")
# TCP keepalive — detect stale IPTV connections faster
ADBShell.prop_set("net.ipv4.tcp_keepalive_intvl", "30")
ADBShell.prop_set("net.ipv4.tcp_keepalive_probes", "3")
Logger.log(" ✓ TCP keepalive: 30s interval, 3 probes")
Logger.success("TCP stack optimized for 4K streaming")
def set_private_dns(self, provider: str = "cloudflare") -> None:
"""
Configure Private DNS (DNS-over-TLS).
Cast-safe note: Private DNS only affects unicast A/AAAA queries.
mDNS (.local domains, port 5353 multicast) is NOT routed through
Private DNS and is completely unaffected by this setting.
Chromecast discovery WILL continue to work normally.
"""
Logger.header("🔒 PRIVATE DNS — DNS-over-TLS Configuration")
Logger.shield(
"Private DNS (DoT) only affects unicast DNS. "
"Chromecast mDNS discovery uses multicast UDP — unaffected."
)
hostname = self.DNS_PROVIDERS.get(provider.lower())
if not hostname:
Logger.error(f"Unknown DNS provider: {provider}. Choose: {list(self.DNS_PROVIDERS)}")
return
ADBShell.setting_put("global", "private_dns_mode", "hostname")
ADBShell.setting_put("global", "private_dns_specifier", hostname)
Logger.success(f"Private DNS: {hostname}")
# Flush DNS cache (unicast only — mDNS cache unaffected)
ADBShell.run("ndc resolver flushnet 100", ignore_errors=True, silent=True)
Logger.log(" ✓ Unicast DNS cache flushed")
# ─────────────────────────────────────────────────────────────────────────────
# SAFE DEBLOATER — Chromecast gate enforced
# ─────────────────────────────────────────────────────────────────────────────
class SafeDebloater:
"""
Removes bloatware while enforcing ChromecastProtection at every step.
Uses pm disable-user --user 0 (reversible) — NOT pm uninstall.
Any package in ChromecastProtection.PROTECTED_PACKAGES is skipped with
an explicit log entry explaining the engineering reason.
"""
def run(self) -> None:
Logger.header("🗑 SAFE DEBLOAT — Chromecast Services Protected")
disabled, protected, skipped, failed = 0, 0, 0, 0
for pkg, reason, cast_impact in SAFE_DEBLOAT_DB:
# ── Chromecast gate: absolute check before any action ──────────
if ChromecastProtection.is_protected(pkg):
protected += 1
Logger.shield(f"PROTECTED — skipped: {pkg}")
Logger.log(
f" └─ {ChromecastProtection.get_reason(pkg)}", "cyan"
)
continue
# Idempotency: already disabled?
if not ADBShell.pkg_enabled(pkg):
skipped += 1
Logger.log(f" ○ Already disabled: {pkg}", "cyan")
continue
result = ADBShell.run(
f"pm disable-user --user 0 {pkg}",
ignore_errors=True, silent=True,
)
if "disabled" in result.lower() or result == "":
disabled += 1
Logger.success(f"Disabled: {pkg}")
Logger.log(f" └─ {reason} | Cast impact: {cast_impact}", "cyan")
else:
failed += 1
Logger.warning(f"Could not disable: {pkg}")
Logger.header(
f"DEBLOAT DONE: {disabled} disabled | {protected} protected (Cast) | "
f"{skipped} already off | {failed} failed"
)
def restore_package(self, pkg: str) -> None:
"""Re-enable a single package (emergency rollback)."""
ADBShell.run(f"pm enable --user 0 {pkg}", ignore_errors=True)
Logger.success(f"Re-enabled: {pkg}")
# ─────────────────────────────────────────────────────────────────────────────
# MEMORY & LMK OPTIMIZER
# ─────────────────────────────────────────────────────────────────────────────
class MemoryOptimizer:
"""LMK + RAM management for 1.4GB constrained device."""
def apply_lmk_profile(self) -> None:
Logger.header("🧹 LMK — SmartTube + Cast Priority Memory Profile")
lmk_props = [
("ro.lmk.critical", "0"), # Kill only at true critical low memory
("ro.lmk.kill_heaviest_task", "true"), # Kill largest non-protected process
("ro.lmk.downgrade_pressure", "100"), # Aggressive downgrade before kill
("ro.lmk.upgrade_pressure", "50"), # Promote cached processes quickly
("ro.lmk.use_psi", "true"), # Use Pressure Stall Info for smarter kills
("ro.lmk.filecache_min_kb", "51200"), # Keep 50MB for VP9 segment file cache
]
for key, value in lmk_props:
ADBShell.prop_set(key, value)
Logger.log(f" ✓ {key} = {value}")
# 3 background processes: OS services + Cast daemon + Launcher
# SmartTube runs in foreground → exempt from this limit
ADBShell.setting_put("global", "background_process_limit", "3")
Logger.log(" ✓ background_process_limit = 3")
Logger.success("LMK configured: SmartTube + Cast services protected from OOM")
def deep_clean(self) -> None:
"""
Aggressive RAM release — Cast services verified alive afterward.
Belt-and-suspenders: restore_cast_services() called post-clean.
"""
Logger.header("🔄 DEEP CLEAN — Cast-Safe RAM Release")
Logger.log(" Releasing background apps (am kill-all)...")
ADBShell.run("am kill-all", ignore_errors=True)
Logger.log(" Trimming app caches (2GB limit)...")
ADBShell.run("pm trim-caches 2G", ignore_errors=True)
ADBShell.run("dumpsys batterystats --reset", ignore_errors=True, silent=True)
Logger.log(" Dropping kernel page cache (root required)...")
ADBShell.run_root("sync && echo 3 > /proc/sys/vm/drop_caches")
# Critical: restore Cast services after kill-all may have killed them
Logger.shield("Re-verifying Cast services after cleanup...")
ChromecastServiceManager.restore()
Logger.success("Deep clean complete — Cast services re-verified active")
# ─────────────────────────────────────────────────────────────────────────────
# UI OPTIMIZER
# ─────────────────────────────────────────────────────────────────────────────
class UIOptimizer:
def apply(self, scale: float = 0.5) -> None:
"""0.5x animation scale: optimal for Android TV (0=broken feel, 1=sluggish on A15)."""
Logger.header(f"🎨 UI — Animation Scale {scale}x + TV Recommendations Off")
for key in [
"window_animation_scale",
"transition_animation_scale",
"animator_duration_scale",
]:
ADBShell.setting_put("global", key, str(scale))
Logger.log(f" ✓ {key} = {scale}x")
# Disable TV content recommendations (background HTTP polling, wastes RAM/CPU)
ADBShell.run("settings put secure tv_disable_recommendations 1", ignore_errors=True)
ADBShell.run("settings put secure tv_enable_preview_programs 0", ignore_errors=True)
ADBShell.run("settings put secure tv_watch_next_enabled 0", ignore_errors=True)
Logger.log(" ✓ TV recommendations + preview programs disabled")
Logger.success("UI responsiveness optimized")
# ─────────────────────────────────────────────────────────────────────────────
# SHIZUKU MANAGER
# ─────────────────────────────────────────────────────────────────────────────
class ShizukuManager:
"""Deploy Shizuku privilege manager using ADB bootstrap method."""
_BOOTSTRAP_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 &"
)
def deploy(self) -> None:
Logger.header("🔑 SHIZUKU — Privilege Engine Deployment")
pkg = DeviceSpec.SHIZUKU_PKG
if not ADBShell.pkg_enabled(pkg):
Logger.log("Shizuku not installed — downloading...")
apk_local = CACHE_DIR / "shizuku.apk"
if not apk_local.exists():
os.system(f'curl -L -s --retry 3 -o "{apk_local}" "{DeviceSpec.SHIZUKU_APK_URL}"')
if ADBShell.push(str(apk_local), "/data/local/tmp/s.apk"):
ADBShell.run("pm install -r -g /data/local/tmp/s.apk", ignore_errors=True)
ADBShell.run("rm /data/local/tmp/s.apk", ignore_errors=True, silent=True)
Logger.success("Shizuku installed")
else:
Logger.success("Shizuku already installed")
Logger.log("Starting Shizuku bootstrap server...")
ADBShell.run(self._BOOTSTRAP_CMD, ignore_errors=True)
time.sleep(3)
Logger.success("Shizuku server started — verify status in Shizuku app")
# ─────────────────────────────────────────────────────────────────────────────
# APP INSTALLER
# ─────────────────────────────────────────────────────────────────────────────
class AppInstaller:
"""Idempotent APK installer — always checks before downloading."""
def install_smarttube(self, beta: bool = False) -> bool:
pkg = DeviceSpec.SMARTTUBE_PKG
url = DeviceSpec.SMARTTUBE_BETA_URL if beta else DeviceSpec.SMARTTUBE_STABLE_URL
name = "SmartTube Beta" if beta else "SmartTube Stable"
Logger.header(f"📦 {name} — INSTALLER")
if ADBShell.pkg_enabled(pkg):
Logger.success(f"{name} already installed — skipping download")
return True
apk_local = CACHE_DIR / f"smarttube_{'beta' if beta else 'stable'}.apk"
if not apk_local.exists():
Logger.log(f"Downloading {name}...")
if os.system(f'curl -L -s --retry 3 -o "{apk_local}" "{url}"') != 0:
Logger.error("Download failed")
return False
remote = "/data/local/tmp/smarttube.apk"
if not ADBShell.push(str(apk_local), remote):
Logger.error("APK push failed")
return False
result = ADBShell.run(f"pm install -r -g {remote}", ignore_errors=True)
ADBShell.run(f"rm {remote}", ignore_errors=True, silent=True)
if "success" in result.lower():
Logger.success(f"{name} installed successfully")
return True
Logger.error(f"Installation failed: {result}")
return False
# ─────────────────────────────────────────────────────────────────────────────
# DIAGNOSTICS
# ─────────────────────────────────────────────────────────────────────────────
class Diagnostics:
@staticmethod
def full_report() -> None:
Logger.header("📊 SYSTEM DIAGNOSTICS — Full Health Report")
# CPU load
loadavg = ADBShell.run("cat /proc/loadavg", silent=True)
if loadavg:
parts = loadavg.split()[:3]
Logger.log(f" CPU Load (1m/5m/15m): {' / '.join(parts)}")
# RAM
meminfo = ADBShell.run("cat /proc/meminfo", silent=True)
if meminfo:
fields: Dict[str, int] = {}
for line in meminfo.splitlines():
p = line.split()
if len(p) >= 2 and p[1].isdigit():
fields[p[0].rstrip(":")] = int(p[1])
total = fields.get("MemTotal", 0) // 1024
free = fields.get("MemFree", 0) // 1024
avail = fields.get("MemAvailable", 0) // 1024
cached = fields.get("Cached", 0) // 1024
pct = avail / total * 100 if total else 0
color = "success" if pct > 30 else ("warning" if pct > 15 else "error")
Logger.log(f" RAM Total : {total} MB")
Logger.log(f" RAM Free : {free} MB")
Logger.log(f" RAM Available : {avail} MB ({pct:.1f}%)", color)
Logger.log(f" Cached : {cached} MB")
# Thermal
for zone in range(4):
raw = ADBShell.run(
f"cat /sys/class/thermal/thermal_zone{zone}/temp", silent=True
)
if raw and raw.lstrip("-").isdigit():
temp_c = int(raw) / 1000
color = "success" if temp_c < 55 else ("warning" if temp_c < 70 else "error")
Logger.log(f" Thermal zone{zone}: {temp_c:.1f}°C", color)
# Storage
storage = ADBShell.run("df -h /data", silent=True)
if storage:
lines = storage.splitlines()
if len(lines) > 1:
Logger.log(f" Storage /data : {lines[1]}")
# Network
ping = ADBShell.run("ping -c 2 -W 2 1.1.1.1", ignore_errors=True, silent=True)
if "2 received" in ping:
Logger.success(" Internet: OK (Cloudflare reachable)")
elif "1 received" in ping:
Logger.warning(" Internet: Degraded (packet loss)")
else:
Logger.error(" Internet: OFFLINE")
# Cast service audit (compact)
Logger.log("\n --- Chromecast Service Status ---")
for pkg in list(ChromecastProtection.PROTECTED_PACKAGES)[:6]:
active = ADBShell.pkg_enabled(pkg)
icon = "🛡✓" if active else "🛡✗"
color = "success" if active else "error"
Logger.log(f" {icon} {pkg.split('.')[-1]}", color)
# SmartTube
if ADBShell.pkg_enabled(DeviceSpec.SMARTTUBE_PKG):
Logger.success(" SmartTube: Installed")
else:
Logger.warning(" SmartTube: Not installed")
# Renderer
renderer = ADBShell.prop_get("debug.hwui.renderer")
Logger.log(f" HWUI Renderer : {renderer or 'default'}")
tunnel = ADBShell.prop_get("media.tunneled-playback.enable")
color = "success" if tunnel == "true" else "warning"
Logger.log(f" Tunnel Mode : {tunnel or 'not set'}", color)
@staticmethod
def codec_report() -> None:
Logger.header("🎬 CODEC AVAILABILITY — BCM7362 VPU Matrix")
codecs = [
("H.264 AVC", "OMX.brcm.video.h264.decoder", "HW", "✓ 4K@30fps"),
("H.265 HEVC", "OMX.brcm.video.h265.decoder", "HW", "✓ 4K@60fps"),
("VP9 prof.2", "OMX.brcm.video.vp9.decoder", "HW", "✓ 4K@60fps ← RECOMMENDED"),
("AV1", "c2.android.av1.decoder", "SOFTWARE", "✗ DO NOT USE — CPU killer"),
]
for name, component, hw_sw, status in codecs:
level = "success" if hw_sw == "HW" else "error"
Logger.log(f" [{hw_sw:8s}] {name:12s} {status}", level)
Logger.log(f" Component: {component}", "cyan")
# ─────────────────────────────────────────────────────────────────────────────
# MAIN ORCHESTRATOR
# ─────────────────────────────────────────────────────────────────────────────
class PlayboxTitaniumULTRA:
"""
Master orchestration class.
Every optimization mode enforces ChromecastProtection throughout.
"""
def __init__(self, device: str) -> None:
self.device = device
self.smarttube = SmartTubeOptimizer()
self.broadcom = BroadcomTuner()
self.audio = AudioOptimizer()
self.network = NetworkOptimizer()
self.memory = MemoryOptimizer()
self.ui = UIOptimizer()
self.debloater = SafeDebloater()
self.cast = ChromecastServiceManager()
self.installer = AppInstaller()
self.shizuku = ShizukuManager()
self.diag = Diagnostics()
# ── Composite actions ────────────────────────────────────────────────────
def _video_pipeline(self) -> None:
self.smarttube.optimize_exoplayer_pipeline()
self.smarttube.force_tunnel_mode()
def _broadcom_full(self) -> None:
self.broadcom.apply_gpu_tweaks()
self.broadcom.apply_vpu_tweaks()
self.broadcom.set_performance_governor()
def _codec_protection(self) -> None:
self.smarttube.suppress_av1_software_decode()
self.smarttube.tune_dalvik_heap()
def _network_full(self) -> None:
self.network.apply_tcp_tuning()
self.network.set_private_dns("cloudflare")
# ── Banner & Menu ────────────────────────────────────────────────────────
def _banner(self) -> None:
c = Logger.COLORS
print(f"""
{c['header']}{c['bold']}╔══════════════════════════════════════════════════════════════════════╗
║ PLAYBOX TITANIUM v{VERSION}
║ Sagemcom DCTIW362P │ Android TV 9 │ Broadcom BCM7362
╠══════════════════════════════════════════════════════════════════════╣
║ 🎬 SmartTube VP9 HW Pipeline 🚇 Tunnel Mode 🛡 Cast Protected
║ 🧠 Dalvik SmartTube Profile ⚙ CPU Performance Governor
╚══════════════════════════════════════════════════════════════════════╝
{c['reset']} Connected: {c['cyan']}{self.device}{c['reset']}
""")
def _menu(self) -> None:
c = Logger.COLORS
while True:
os.system("clear")
self._banner()
print(f"""{c['bold']}╔══════════════════════════════════════════════════════════════════╗{c['reset']}
{c['success']}VIDEO PERFORMANCE{c['reset']}
{c['success']}1.{c['reset']} SmartTube ExoPlayer + Tunnel Mode Pipeline
{c['success']}2.{c['reset']} Broadcom GPU + VPU + CPU Governor
{c['success']}3.{c['reset']} Audio A/V Sync Fix (HDMI desync)
{c['success']}4.{c['reset']} AV1 Suppression + Dalvik Heap (VP9 HW path)
{c['success']}5.{c['reset']} AOT Compile SmartTube (JIT burst elimination)
{c['header']}CHROMECAST PROTECTION{c['reset']}
{c['success']}6.{c['reset']} 🛡 Audit Cast Services (health check)
{c['success']}7.{c['reset']} 🛡 Restore Cast Services (repair broken Cast)
{c['success']}8.{c['reset']} 📡 Cast mDNS/Multicast Network Tuning
{c['success']}9.{c['reset']} 🎯 Cast Process OOM Hardening
{c['success']}P.{c['reset']} 🔍 Show Chromecast Protection Registry
{c['info']}SYSTEM{c['reset']}
{c['info']}10.{c['reset']} Network TCP + Private DNS (Cloudflare)
{c['info']}11.{c['reset']} Safe Debloat (Cast services PROTECTED)
{c['info']}12.{c['reset']} LMK Memory Profile (SmartTube + Cast priority)
{c['info']}13.{c['reset']} UI Responsiveness (0.5x animations)
{c['info']}14.{c['reset']} Deep Clean RAM (Cast-safe + re-verify)
{c['warning']}TOOLS{c['reset']}
{c['warning']}15.{c['reset']} Full System Diagnostics
{c['warning']}16.{c['reset']} Codec Availability Report
{c['warning']}17.{c['reset']} Install SmartTube Stable
{c['warning']}18.{c['reset']} Deploy Shizuku
{c['cyan']}AUTO MODES{c['reset']}
{c['cyan']}20.{c['reset']} 🚀 SMARTTUBE ULTRA (video-focused, Cast safe)
{c['cyan']}21.{c['reset']} 🏆 FULL SYSTEM ULTRA (all modules, Cast safe)
{c['error']}0.{c['reset']} Exit
{c['bold']}╚══════════════════════════════════════════════════════════════════╝{c['reset']}""")
choice = input(f"\n{c['cyan']}Choice > {c['reset']}").strip().lower()
dispatch: Dict[str, Callable] = {
"1": self._video_pipeline,
"2": self._broadcom_full,
"3": self.audio.fix_av_sync_and_pipeline,
"4": self._codec_protection,
"5": self.smarttube.compile_aot,
"6": self.cast.audit,
"7": self.cast.restore,
"8": self.cast.optimize_cast_network,
"9": self.cast.harden_cast_processes,
"p": ChromecastProtection.print_registry,
"10": self._network_full,
"11": self.debloater.run,
"12": self.memory.apply_lmk_profile,
"13": self.ui.apply,
"14": self.memory.deep_clean,
"15": self.diag.full_report,
"16": self.diag.codec_report,
"17": self.installer.install_smarttube,
"18": self.shizuku.deploy,
"20": self.run_smarttube_ultra,
"21": self.run_full_ultra,
"0": self._exit,
}
fn = dispatch.get(choice)
if fn:
fn()
else:
Logger.warning("Invalid choice")
if choice != "0":
input(f"\n{c['cyan']}Press Enter...{c['reset']}")
def _exit(self) -> None:
Logger.save_log()
Logger.log(f"Session saved: {LOG_FILE}", "cyan")
sys.exit(0)
# ── Auto modes ───────────────────────────────────────────────────────────
def run_smarttube_ultra(self) -> None:
"""
SmartTube Ultra Mode.
Focuses on maximum video playback performance.
Chromecast protection enforced at start, hardened at end.
"""
Logger.header("🚀 SMARTTUBE ULTRA MODE — Maximum Video Performance")
pipeline: List[Tuple[str, Callable]] = [
("Chromecast Audit (pre-check)", self.cast.audit),
("Broadcom GPU + VPU + CPU", self._broadcom_full),
("ExoPlayer + Tunnel Mode Pipeline", self._video_pipeline),
("AV1 Suppression + Dalvik Heap", self._codec_protection),
("Audio A/V Sync Fix", self.audio.fix_av_sync_and_pipeline),
("LMK Memory Profile", self.memory.apply_lmk_profile),
("UI Responsiveness", self.ui.apply),
("Cast mDNS Network Tuning", self.cast.optimize_cast_network),
("Cast Process Hardening", self.cast.harden_cast_processes),
("SmartTube AOT Compilation", self.smarttube.compile_aot),
("Cast Services Restore (final)", self.cast.restore),
]
total = len(pipeline)
for idx, (name, fn) in enumerate(pipeline, 1):
Logger.log(f"\n[{idx}/{total}] {name}...", "cyan")
fn()
time.sleep(0.4)
Logger.header("🎉 SMARTTUBE ULTRA COMPLETE")
Logger.success("VP9 HW decode + Tunnel Mode + AOT compiled + Cast Protected")
Logger.log(
"ACTION REQUIRED in SmartTube: Settings → Player → Video codec → VP9",
"warning"
)
Logger.log("Reboot recommended in ~5 minutes for full effect", "warning")
Logger.save_log()
def run_full_ultra(self) -> None:
"""
Full System Ultra — all modules activated.
Chromecast protection enforced at every debloat/clean step.
"""
Logger.header("🏆 FULL SYSTEM ULTRA — Complete Optimization")
pipeline: List[Tuple[str, Callable]] = [
("System Diagnostics (baseline)", self.diag.full_report),
("Chromecast Audit (pre-check)", self.cast.audit),
("Broadcom GPU + VPU + CPU", self._broadcom_full),
("ExoPlayer + Tunnel Mode", self._video_pipeline),
("AV1 Suppression + Dalvik Heap", self._codec_protection),
("Audio A/V Sync Fix", self.audio.fix_av_sync_and_pipeline),
("TCP Network + Cloudflare DNS", self._network_full),
("Safe Debloat (Cast Protected)", self.debloater.run),
("LMK Memory Profile", self.memory.apply_lmk_profile),
("UI Responsiveness", self.ui.apply),
("Cast mDNS Network Tuning", self.cast.optimize_cast_network),
("Cast Process OOM Hardening", self.cast.harden_cast_processes),
("SmartTube AOT Compilation", self.smarttube.compile_aot),
("Deep Clean RAM (Cast-safe)", self.memory.deep_clean),
("Final Chromecast Audit", self.cast.audit),
]
total = len(pipeline)
for idx, (name, fn) in enumerate(pipeline, 1):
Logger.log(f"\n[{idx}/{total}] {name}...", "cyan")
fn()
time.sleep(0.3)
Logger.header("🏆 FULL SYSTEM ULTRA COMPLETE")
Logger.success("All optimizations applied. Chromecast Built-in: PROTECTED ✓")
Logger.success("SmartTube: VP9 HW + Tunnel Mode + AOT compiled ✓")
Logger.log(f"Reboot: adb -s {self.device} reboot", "warning")
Logger.save_log()
# ─────────────────────────────────────────────────────────────────────────────
# CLI ENTRY POINT
# ─────────────────────────────────────────────────────────────────────────────
def _build_parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(
description=f"Playbox Titanium v{VERSION} — Sagemcom DCTIW362P",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
EXAMPLES:
# Interactive menu (default)
python3 Autopilot_11_ULTRA_SmartTube.py
# SmartTube Ultra (video-focused)
python3 Autopilot_11_ULTRA_SmartTube.py --device 192.168.1.3:5555 --smarttube-ultra
# Full system optimization
python3 Autopilot_11_ULTRA_SmartTube.py --device 192.168.1.3:5555 --full-ultra
# Emergency: restore broken Chromecast
python3 Autopilot_11_ULTRA_SmartTube.py --cast-restore
# Audit Cast services only
python3 Autopilot_11_ULTRA_SmartTube.py --cast-audit
# Diagnostics
python3 Autopilot_11_ULTRA_SmartTube.py --diagnostics
""",
)
p.add_argument("--device", default=None,
help="ADB target IP:PORT (default: auto-detect)")
p.add_argument("--smarttube-ultra", action="store_true",
help="SmartTube Ultra Mode")
p.add_argument("--full-ultra", action="store_true",
help="Full system optimization")
p.add_argument("--cast-audit", action="store_true",
help="Audit Chromecast services")
p.add_argument("--cast-restore", action="store_true",
help="Re-enable all Chromecast services (emergency)")
p.add_argument("--diagnostics", action="store_true",
help="Full system diagnostics")
p.add_argument("--codecs", action="store_true",
help="Codec availability report")
p.add_argument("--install-smarttube", action="store_true",
help="Install SmartTube")
p.add_argument("--beta", action="store_true",
help="Use SmartTube Beta channel")
return p
def main() -> None:
args = _build_parser().parse_args()
# Device resolution
device = args.device
if not device:
device = ADBShell.detect_device()
if device:
Logger.success(f"Auto-detected: {device}")
else:
Logger.warning(f"No device detected, using default: {DEFAULT_ADB_HOST}")
device = DEFAULT_ADB_HOST
if not ADBShell.connect(device):
Logger.error(f"Cannot connect to {device}")
sys.exit(1)
app = PlayboxTitaniumULTRA(device)
# CLI dispatch
if args.cast_restore:
ChromecastServiceManager.restore()
elif args.cast_audit:
ChromecastServiceManager.audit()
elif args.diagnostics:
app.diag.full_report()
elif args.codecs:
app.diag.codec_report()
elif args.install_smarttube:
app.installer.install_smarttube(beta=args.beta)
elif args.smarttube_ultra:
app.run_smarttube_ultra()
elif args.full_ultra:
app.run_full_ultra()
else:
app._banner()
app._menu()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print()
Logger.warning("Interrupted (Ctrl+C)")
Logger.save_log()
sys.exit(0)
except Exception as exc:
Logger.error(f"Unexpected error: {exc}")
import traceback
traceback.print_exc()
sys.exit(1)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
╔══════════════════════════════════════════════════════════════════════════════╗
║ PLAYBOX TITANIUM v12.0 ULTRA — Self-Diagnostic + Auto-Repair Edition ║
║ Target : Sagemcom DCTIW362P | Android TV 9 (API 28) | Broadcom m362 ║
║ Focus : SmartTube HW, Chromecast, Self-Diagnostics, Auto-Repair ║
║ Author : Anonymousik / SecFerro Division ║
║ Version : 12.0-ULTRA-SelfHeal ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ HARDWARE PROFILE (real ADB session 192.168.1.3:5555): ║
║ RAM : 1459 MB total | ~534 MB available ║
║ FLASH : 3.9 GB | 53% used (/data) ║
║ SoC : Broadcom BCM7362 | Dual-core Cortex-A15 @ 1.0 GHz ║
║ VPU : H.264 ✓ | H.265 ✓ | VP9 ✓ | AV1 ✗ (SW only) ║
║ GPU : Broadcom VideoCore (GLES 3.1, NO Vulkan) ║
║ Display : HDMI 2.0a, CEC v1.4, 4K@60fps, HDR10 ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ CHANGELOG v12 vs v11: ║
║ [FIX] DNS: one.one.one.one (DoT) + net.dns1/net.dns2 setprop ║
║ [FIX] SmartTube pkg: org.smarttube.stable (verified from ps output) ║
║ [FIX] HDMI addr: persist.sys.hdmi.addr.playback=11 (BCM Nexus correct) ║
║ [NEW] debug.stagefright.ccodec=1 (C2 pipeline), skiaglthreaded renderer ║
║ [NEW] LMK minfree /sys tuning, swappiness=10, VFS pressure=50 ║
║ [NEW] TCP kernel params (SACK, timestamps, window scaling, BBR attempt) ║
║ [NEW] I/O scheduler=deadline, read_ahead_kb=512 ║
║ [NEW] BCM Nexus props (persist.nx.hdmi.*, persist.nx.vidout.50hz) ║
║ [NEW] InteractiveDiagnostics — 8 service categories, self-healing ║
║ [NEW] AutoRepair — detects broken sectors, re-downloads + reinstalls APKs ║
║ [NEW] CastRepair — restores debloat.sh damage (mediashell kill prevention) ║
╚══════════════════════════════════════════════════════════════════════════════╝
"""
from __future__ import annotations
import os
import sys
import subprocess
import time
import json
import hashlib
import argparse
import tempfile
import shutil
from pathlib import Path
from typing import Optional, List, Dict, Tuple, Callable, Any
from dataclasses import dataclass, field
from enum import Enum, auto
# ─────────────────────────────────────────────────────────────────────────────
# CONSTANTS
# ─────────────────────────────────────────────────────────────────────────────
VERSION = "12.0-ULTRA-SelfHeal"
DEFAULT_ADB_HOST = "192.168.1.3:5555"
CACHE_DIR = Path.home() / ".playbox_cache"
BACKUP_DIR = CACHE_DIR / "backups_v12"
LOG_FILE = CACHE_DIR / "autopilot_v12.log"
REPAIR_LOG = CACHE_DIR / "repair_v12.log"
for d in (CACHE_DIR, BACKUP_DIR):
d.mkdir(parents=True, exist_ok=True)
# ─────────────────────────────────────────────────────────────────────────────
# STATUS ENUM
# ─────────────────────────────────────────────────────────────────────────────
class Status(Enum):
OK = auto()
WARN = auto()
BROKEN = auto()
MISSING = auto()
UNKNOWN = auto()
# ─────────────────────────────────────────────────────────────────────────────
# DEVICE SPEC (verified from real ps + getprop session)
# ─────────────────────────────────────────────────────────────────────────────
class DeviceSpec:
MODEL = "dctiw362p"
HARDWARE = "m362"
ANDROID_VERSION = "9"
API_LEVEL = "28"
# BCM7362 VPU — confirmed from OMX enumeration on this chipset family
CODEC_HW_H264 = "OMX.brcm.video.h264.decoder"
CODEC_HW_HEVC = "OMX.brcm.video.h265.decoder"
CODEC_HW_VP9 = "OMX.brcm.video.vp9.decoder"
CODEC_SW_AV1 = "c2.android.av1.decoder" # HW not present → AVOID
# Dalvik heap for ~534 MB available
DALVIK_HEAP_SIZE = "256m"
DALVIK_HEAP_GROWTH = "128m"
DALVIK_HEAP_START = "16m"
DALVIK_HEAP_MIN_FREE = "2m"
DALVIK_HEAP_MAX_FREE = "8m"
DALVIK_HEAP_TARGET = "0.75"
# ── VERIFIED PACKAGE NAMES (from real adb shell ps output) ───────────────
# CRITICAL: org.smarttube.stable — NOT com.teamsmart.videomanager.tv
# Real ps: u0_a89 6624 2996 org.smarttube.stable
SMARTTUBE_PKG_STABLE = "org.smarttube.stable"
SMARTTUBE_PKG_BETA = "org.smarttube.beta"
SMARTTUBE_PKG_LEGACY = "com.liskovsoft.smarttubetv" # old package, still accepted
SMARTTUBE_STABLE_URL = "https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_stable.apk"
SMARTTUBE_BETA_URL = "https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_beta.apk"
# Verified from real ps: u0_a88 26563 com.spocky.projengmenu
PROJECTIVY_PKG = "com.spocky.projengmenu"
PROJECTIVY_URL = "https://github.com/spocky/projectivy-launcher/releases/latest/download/Projectivy_Launcher.apk"
SHIZUKU_PKG = "moe.shizuku.privileged.api"
SHIZUKU_URL = "https://github.com/RikkaApps/Shizuku/releases/download/v13.5.4/shizuku-v13.5.4-release.apk"
# DNS: correct DoT hostnames (verified working on Android 9)
DNS_PROVIDERS: Dict[str, Tuple[str, str, str]] = {
# name: (dot_hostname, ip_primary, ip_secondary)
"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"),
}
# ─────────────────────────────────────────────────────────────────────────────
# CHROMECAST PROTECTION REGISTRY
# ─────────────────────────────────────────────────────────────────────────────
class ChromecastProtection:
"""
Protected package whitelist — these are NEVER disabled/modified.
IMPORTANT: real debloat.sh on device lists apps.mediashell as "safe to disable".
This is WRONG. It IS the Chromecast daemon. We defend against this mistake.
ALSO: gms.cast.receiver found in debloat.sh — also protected here.
"""
PROTECTED: Dict[str, str] = {
"com.google.android.apps.mediashell":
"Cast daemon — THIS IS CHROMECAST BUILT-IN. NEVER disable.",
"com.google.android.gms":
"GMS — Cast SDK host, auth, SessionManager.",
"com.google.android.gsf":
"Google Services Framework — GMS dependency.",
"com.google.android.nearby":
"Nearby — mDNS/BLE Cast discovery.",
"com.google.android.gms.cast.receiver":
"Cast Receiver Framework — part of GMS Cast stack.",
"com.google.android.tv.remote.service":
"TV Remote Service — Cast session UI & input.",
"com.google.android.tvlauncher":
"TV Launcher — Cast ambient mode, intent handoff.",
"com.google.android.configupdater":
"Config Updater — Cast TLS certificates.",
"com.google.android.wifidisplay":
"WiFi Display — Miracast/Cast fallback.",
"com.android.networkstack":
"Network Stack — IGMP multicast for mDNS.",
"com.android.networkstack.tethering":
"Network Tethering — multicast routing.",
}
@classmethod
def is_protected(cls, pkg: str) -> bool:
return pkg in cls.PROTECTED
@classmethod
def get_reason(cls, pkg: str) -> str:
return cls.PROTECTED.get(pkg, "")
@classmethod
def print_all(cls) -> None:
Logger.header("🛡 CHROMECAST PROTECTION REGISTRY")
for pkg, reason in cls.PROTECTED.items():
Logger.shield(f"{pkg}")
Logger.log(f" └─ {reason}", "cyan")
# ─────────────────────────────────────────────────────────────────────────────
# LOGGER
# ─────────────────────────────────────────────────────────────────────────────
class Logger:
C = {
"info": "\033[94m",
"success": "\033[92m",
"warning": "\033[93m",
"error": "\033[91m",
"header": "\033[95m",
"cyan": "\033[96m",
"bold": "\033[1m",
"reset": "\033[0m",
"dim": "\033[2m",
}
_lines: List[str] = []
@classmethod
def _emit(cls, msg: str, level: str) -> None:
ts = time.strftime("%H:%M:%S")
c = cls.C.get(level, cls.C["info"])
print(f"{c}[{ts}] {msg}{cls.C['reset']}")
cls._lines.append(f"[{ts}][{level.upper()}] {msg}")
@classmethod
def log(cls, m: str, l: str = "info") -> None: cls._emit(m, l)
@classmethod
def success(cls, m: str) -> None: cls._emit(f"✓ {m}", "success")
@classmethod
def warning(cls, m: str) -> None: cls._emit(f"⚠ {m}", "warning")
@classmethod
def error(cls, m: str) -> None: cls._emit(f"✗ {m}", "error")
@classmethod
def shield(cls, m: str) -> None: cls._emit(f"🛡 {m}", "success")
@classmethod
def repair(cls, m: str) -> None: cls._emit(f"🔧 {m}", "warning")
@classmethod
def header(cls, msg: str) -> None:
sep = "═" * 74
print(f"\n{cls.C['header']}{cls.C['bold']}{sep}\n {msg}\n{sep}{cls.C['reset']}\n")
@classmethod
def section(cls, msg: str) -> None:
print(f"\n{cls.C['cyan']} ── {msg} ──{cls.C['reset']}")
@classmethod
def save(cls) -> None:
try:
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(f"\n{'─'*60}\nSESSION {time.strftime('%Y-%m-%d %H:%M:%S')} | v{VERSION}\n")
f.write("\n".join(cls._lines) + "\n")
except OSError:
pass
# ─────────────────────────────────────────────────────────────────────────────
# ADB SHELL INTERFACE
# ─────────────────────────────────────────────────────────────────────────────
class ADB:
"""
Secure ADB executor. No shell=True. Retry on Timeout only.
Escalation: su → rish → plain ADB.
"""
device: Optional[str] = None
TIMEOUT: int = 35
RETRIES: int = 3
@classmethod
def connect(cls, target: str) -> bool:
try:
r = subprocess.run(["adb", "connect", target],
capture_output=True, text=True, timeout=10)
if "connected" in r.stdout.lower():
cls.device = target
Logger.success(f"ADB connected: {target}")
return True
Logger.error(f"ADB failed: {r.stdout.strip()}")
return False
except FileNotFoundError:
Logger.error("'adb' not found. Install Android Platform Tools.")
sys.exit(1)
except subprocess.TimeoutExpired:
Logger.error(f"ADB connect timeout: {target}")
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 run(cls, cmd: str, silent: bool = False) -> str:
if not cls.device:
return ""
for attempt in range(cls.RETRIES):
try:
out = subprocess.check_output(
["adb", "-s", cls.device, "shell", cmd],
stderr=subprocess.STDOUT, text=True, timeout=cls.TIMEOUT)
return out.strip()
except subprocess.TimeoutExpired:
if attempt < cls.RETRIES - 1:
time.sleep(1.5)
elif not silent:
Logger.warning(f"Timeout [{attempt+1}]: {cmd[:55]}")
except subprocess.CalledProcessError as e:
return (e.output or "").strip()
except Exception as e:
if not silent:
Logger.error(f"ADB: {e}")
return ""
return ""
@classmethod
def run_root(cls, cmd: str) -> str:
for prefix in (f'su -c "{cmd}"', f'rish -c "{cmd}"'):
r = cls.run(prefix, silent=True)
if r and "not found" not in r and "permission denied" not in r.lower():
return r
return cls.run(cmd)
@classmethod
def push(cls, local: str, remote: str) -> bool:
try:
subprocess.check_call(
["adb", "-s", cls.device, "push", local, remote],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=120)
return True
except Exception:
return False
@classmethod
def prop(cls, key: str) -> str:
return cls.run(f"getprop {key}", silent=True)
@classmethod
def setprop(cls, key: str, val: str) -> None:
cls.run(f"setprop {key} {val}", silent=True)
@classmethod
def setting_put(cls, ns: str, key: str, val: str) -> None:
cls.run(f"settings put {ns} {key} {val}", silent=True)
@classmethod
def setting_get(cls, ns: str, key: str) -> str:
return cls.run(f"settings get {ns} {key}", silent=True)
@classmethod
def pkg_enabled(cls, pkg: str) -> bool:
return pkg in cls.run(f"pm list packages -e {pkg}", silent=True)
@classmethod
def pkg_installed(cls, pkg: str) -> bool:
return pkg in cls.run(f"pm list packages {pkg}", silent=True)
@classmethod
def pkg_version(cls, pkg: str) -> str:
out = cls.run(f"dumpsys package {pkg} | grep versionName", silent=True)
if "=" in out:
return out.split("=")[-1].strip()
return "unknown"
@classmethod
def syswrite(cls, path: str, val: str) -> bool:
"""Write to /sys or /proc path via root."""
result = cls.run_root(f"echo {val} > {path}")
# Verify
verify = cls.run_root(f"cat {path}").strip()
return val in verify
# ─────────────────────────────────────────────────────────────────────────────
# DIAGNOSTIC RESULT
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class DiagResult:
category: str
check: str
status: Status
found: str
expected: str = ""
fix_fn: Optional[Callable] = None
detail: str = ""
@property
def is_ok(self) -> bool:
return self.status == Status.OK
@property
def needs_repair(self) -> bool:
return self.status in (Status.BROKEN, Status.MISSING)
# ─────────────────────────────────────────────────────────────────────────────
# APK DOWNLOADER
# ─────────────────────────────────────────────────────────────────────────────
class APKDownloader:
"""Download, verify (size), push and install APK files."""
@staticmethod
def download(url: str, dest: Path, force: bool = False) -> bool:
if dest.exists() and not force:
Logger.log(f" APK cached: {dest.name}")
return True
Logger.log(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:
Logger.error(f" Download failed or file too small: {dest.name}")
if dest.exists():
dest.unlink()
return False
size_mb = dest.stat().st_size / 1_048_576
Logger.success(f" Downloaded: {dest.name} ({size_mb:.1f} MB)")
return True
@staticmethod
def install(apk_local: Path, pkg_hint: str = "") -> bool:
remote = f"/data/local/tmp/{apk_local.name}"
Logger.log(f" Pushing {apk_local.name} → {remote}")
if not ADB.push(str(apk_local), remote):
Logger.error(f" Push failed: {apk_local.name}")
return False
result = ADB.run(f"pm install -r -g --install-reason 1 {remote}", silent=True)
ADB.run(f"rm {remote}", silent=True)
if "success" in result.lower():
Logger.success(f" Installed: {pkg_hint or apk_local.stem}")
return True
Logger.error(f" Install failed: {result[:80]}")
return False
@staticmethod
def download_and_install(url: str, pkg: str, name: str, force: bool = False) -> bool:
safe_name = pkg.replace(".", "_") + ".apk"
local = CACHE_DIR / safe_name
if not APKDownloader.download(url, local, force):
return False
return APKDownloader.install(local, name)
# ─────────────────────────────────────────────────────────────────────────────
# DNS MANAGER (bug-fixed)
# ─────────────────────────────────────────────────────────────────────────────
class DNSManager:
"""
DNS configuration with full dual-path application.
BUG FIX (v10/v11): 'dns.cloudflare.com' was used as DoT hostname — incorrect.
'one.one.one.one' resolves to 1.1.1.1 and is the valid Cloudflare DoT endpoint.
Dual-path approach:
1. setprop net.dns1/net.dns2 — legacy resolver (works immediately, no reboot)
2. settings put global private_dns_* — DoT/DoH encrypted DNS (Android 9+)
Both must be set: setprop affects the current runtime, private_dns_ persists across reboots.
mDNS (Cast discovery, port 5353) is unaffected by either mechanism.
"""
@staticmethod
def set_provider(provider: str = "cloudflare") -> bool:
info = DeviceSpec.DNS_PROVIDERS.get(provider.lower())
if not info:
Logger.error(f"Unknown provider: {provider}")
return False
dot_hostname, ip1, ip2 = info
Logger.header(f"🔒 DNS — {provider.upper()} ({dot_hostname})")
Logger.shield(
"Private DNS (DoT) is unicast-only. mDNS (port 5353, multicast) "
"used by Chromecast is UNAFFECTED."
)
# ── PATH 1: legacy resolver (setprop — immediate, no reboot required) ──
ADB.setprop("net.dns1", ip1)
ADB.setprop("net.dns2", ip2)
ADB.setprop("net.rmnet0.dns1", ip1)
ADB.setprop("net.rmnet0.dns2", ip2)
Logger.log(f" ✓ net.dns1/2 = {ip1} / {ip2}")
# ── PATH 2: Private DNS over TLS (persists reboots) ───────────────────
ADB.setting_put("global", "private_dns_mode", "hostname")
ADB.setting_put("global", "private_dns_specifier", dot_hostname)
Logger.log(f" ✓ Private DNS (DoT): {dot_hostname}")
# ── Flush DNS cache ───────────────────────────────────────────────────
ADB.run("ndc resolver flushnet 100", silent=True)
ADB.run("ndc resolver clearnetdns 100", silent=True)
Logger.log(" ✓ DNS cache flushed")
# ── Connectivity test ─────────────────────────────────────────────────
test = ADB.run(f"ping -c 2 -W 3 {ip1}", silent=True)
if "2 received" in test or "2 packets" in test:
Logger.success(f"DNS reachable: {ip1}")
else:
Logger.warning(f"DNS ping inconclusive (DoT may still work): {ip1}")
return True
@staticmethod
def get_current() -> Tuple[str, str]:
"""Return (dot_hostname, ip1) of currently set DNS."""
dot = ADB.setting_get("global", "private_dns_specifier")
ip1 = ADB.prop("net.dns1")
return dot or "not set", ip1 or "not set"
@staticmethod
def interactive_select() -> None:
"""Interactive DNS provider selection menu."""
Logger.header("🔒 DNS PROVIDER SELECTION")
providers = list(DeviceSpec.DNS_PROVIDERS.keys())
for i, name in enumerate(providers, 1):
dot, ip1, ip2 = DeviceSpec.DNS_PROVIDERS[name]
Logger.log(f" {i}. {name.upper():12s} | DoT: {dot} | IPs: {ip1}/{ip2}")
Logger.log(f" 0. Keep current (no change)")
choice = input(f"\n{Logger.C['cyan']}Select [0-{len(providers)}] > {Logger.C['reset']}").strip()
if choice == "0":
return
try:
idx = int(choice) - 1
if 0 <= idx < len(providers):
DNSManager.set_provider(providers[idx])
else:
Logger.warning("Invalid selection")
except ValueError:
Logger.warning("Invalid input")
# ─────────────────────────────────────────────────────────────────────────────
# VIDEO ENGINE OPTIMIZER
# ─────────────────────────────────────────────────────────────────────────────
class VideoEngineOptimizer:
"""
Comprehensive video pipeline tuning for BCM7362 VPU.
Changes from v11 → v12:
+ debug.stagefright.ccodec = 1 (enables C2 codec framework — modern pipeline)
+ debug.stagefright.omx_default_rank = 0 (force HW OMX over SW C2 when both exist)
+ debug.renderengine.backend = skiaglthreaded (better threading on A15)
+ debug.hwui.use_gpu_pixel_buffers = true (GPU pixel buffers reduce CPU copies)
+ debug.hwui.render_dirty_regions = false (avoid dirty region calc overhead)
+ debug.sf.latch_unsignaled = 1 (reduce SurfaceFlinger latch latency)
+ persist.sys.media.avsync = true (BCM A/V sync hint)
+ persist.sys.hdmi.keep_awake = true (keep HDMI link alive during buffering)
+ drm.service.enabled = true (Widevine/PlayReady explicit enable)
"""
def optimize_codec_pipeline(self) -> None:
Logger.header("🎬 VIDEO CODEC PIPELINE — BCM7362 VPU (C2 + OMX)")
codec_props = [
# Stagefright core
("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"),
# HW priority
("media.acodec.preferhw", "true"),
("media.vcodec.preferhw", "true"),
("media.codec.sw.fallback", "false"),
("media.codec.priority", "1"),
# C2 / OMX rank (new in v12)
# ccodec=1 enables C2 framework, omx_default_rank=0 keeps BCM OMX as primary
("debug.stagefright.ccodec", "1"),
("debug.stagefright.omx_default_rank", "0"),
# DRM
("drm.service.enabled", "true"),
# A/V sync hint (BCM-specific)
("persist.sys.media.avsync", "true"),
("persist.sys.hdmi.keep_awake", "true"),
]
for key, val in codec_props:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
# Media cache: 64KB/128KB/30s format (correct for stagefright)
ADB.setprop("media.stagefright.cache-params", "65536/131072/30")
Logger.log(" ✓ cache-params: 64KB/128KB/30s")
# Extended HLS buffer
abr_props = [
("media.httplive.max-bitrate", "25000000"),
("media.httplive.initial-bitrate", "8000000"),
("media.httplive.max-live-offset", "60"),
("media.httplive.bw-update-interval", "1000"),
]
for key, val in abr_props:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
Logger.success("Codec pipeline configured — BCM7362 VPU HW path active")
def optimize_rendering(self) -> None:
Logger.header("🎮 RENDERING ENGINE — VideoCore + Skia (v12 tuning)")
render_props = [
# Renderer: opengl only — no Vulkan on VideoCore
("debug.hwui.renderer", "skiagl"),
# Threaded Skia backend — uses both A15 cores for render pipeline
("debug.renderengine.backend", "skiaglthreaded"),
# HW composition
("debug.egl.hw", "1"),
("debug.sf.hw", "1"),
# Triple buffer
("debug.gr.numframebuffers", "3"),
# GPU pixel buffers (reduces CPU→GPU copy overhead)
("debug.hwui.use_gpu_pixel_buffers", "true"),
# Disable dirty region calculation (saves CPU per frame)
("debug.hwui.render_dirty_regions", "false"),
# Latch unsignaled: reduce SurfaceFlinger → compositor latency
("debug.sf.latch_unsignaled", "1"),
# Disable SF back-pressure on VPU tunnel output
("debug.sf.disable_backpressure", "1"),
# Buffer age: not useful on VideoCore
("debug.hwui.use_buffer_age", "false"),
# Force GPU for UI window compositing
("persist.sys.ui.hw", "true"),
]
for key, val in render_props:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
ADB.setting_put("global", "force_gpu_rendering", "true")
Logger.log(" ✓ force_gpu_rendering = true")
Logger.success("Rendering engine: skiaglthreaded + VideoCore GPU optimized")
def force_tunnel_mode(self) -> None:
Logger.header("🚇 TUNNEL MODE — VPU → Display Controller Direct Path")
tunnel_props = [
("media.tunneled-playback.enable", "true"),
("media.brcm.tunnel.sessions", "1"),
("media.brcm.hdmi.tunnel", "true"),
# Lock A/V to HDMI sink clock — eliminates drift
("media.brcm.tunnel.clock", "hdmi"),
]
for key, val in tunnel_props:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
Logger.success("Tunnel Mode: VPU→Display direct path (bypass SurfaceFlinger)")
def suppress_av1(self) -> None:
Logger.header("🚫 AV1 SUPPRESSION — Force VP9/H.264 HW Path")
Logger.warning("BCM7362: NO AV1 HW decoder — SW decode causes 100% CPU")
for key, val in [
("debug.stagefright.c2.av1", "0"),
("media.av1.sw.decode.disable", "true"),
("media.codec.av1.disable", "true"),
]:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
Logger.success("AV1 SW decoder blocked — ExoPlayer will negotiate VP9 HW")
c = Logger.C
print(f"""
{c['cyan']}┌──────────────────────────────────────────────────────────┐
│ SmartTube Settings (manual, one-time after launch): │
│ Settings → Player → Video codec → VP9 │
│ Settings → Player → Resolution → 2160p (4K) │
│ Settings → Player → Use tunnel mode → ON │
└──────────────────────────────────────────────────────────┘{c['reset']}
""")
# ─────────────────────────────────────────────────────────────────────────────
# MEMORY + LMK + VM OPTIMIZER
# ─────────────────────────────────────────────────────────────────────────────
class MemoryOptimizer:
"""
Full memory stack tuning.
v12 additions:
+ /sys/module/lowmemorykiller/parameters/minfree (kernel LMK levels)
+ /proc/sys/vm/swappiness = 10 (prefer RAM, no thrashing on eMMC)
+ /proc/sys/vm/vfs_cache_pressure = 50 (keep dentry/inode cache longer)
+ dalvik.vm.heapstartsize = 16m (faster cold start)
+ dalvik.vm.usejitprofiles = true
"""
def apply_dalvik_heap(self) -> None:
Logger.header("🧠 DALVIK/ART HEAP — SmartTube 4K Streaming Profile")
heap_props = [
("dalvik.vm.heapsize", DeviceSpec.DALVIK_HEAP_SIZE),
("dalvik.vm.heapgrowthlimit", DeviceSpec.DALVIK_HEAP_GROWTH),
("dalvik.vm.heapstartsize", DeviceSpec.DALVIK_HEAP_START),
("dalvik.vm.heapminfree", DeviceSpec.DALVIK_HEAP_MIN_FREE),
("dalvik.vm.heapmaxfree", DeviceSpec.DALVIK_HEAP_MAX_FREE),
("dalvik.vm.heaptargetutilization", DeviceSpec.DALVIK_HEAP_TARGET),
("dalvik.vm.usejit", "true"),
("dalvik.vm.usejitprofiles", "true"), # new v12
("dalvik.vm.dex2oat-filter", "speed-profile"),
("dalvik.vm.gctype", "CMS"),
("persist.sys.dalvik.vm.lib.2", "libart.so"),
]
for key, val in heap_props:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
Logger.success("Dalvik heap configured for SmartTube 4K workload")
def apply_lmk_profile(self) -> None:
Logger.header("🧹 LMK — SmartTube + Cast Priority Profile")
# Android props
lmk_props = [
("ro.lmk.critical", "0"),
("ro.lmk.kill_heaviest_task", "true"),
("ro.lmk.downgrade_pressure", "100"),
("ro.lmk.upgrade_pressure", "50"),
("ro.lmk.use_psi", "true"),
("ro.lmk.filecache_min_kb", "51200"),
]
for key, val in lmk_props:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
# Kernel LMK minfree (pages, 4KB each)
# Levels: 48MB,64MB,80MB,96MB,112MB,128MB
minfree = "12288,16384,20480,24576,28672,32768"
wrote = ADB.syswrite("/sys/module/lowmemorykiller/parameters/minfree", minfree)
if wrote:
Logger.success(f"Kernel LMK minfree: {minfree}")
else:
Logger.warning("LMK minfree: /sys not writable (user build) — props applied only")
ADB.setting_put("global", "background_process_limit", "3")
Logger.log(" ✓ background_process_limit = 3")
Logger.success("LMK profile active: SmartTube + Cast protected from OOM")
def apply_vm_tuning(self) -> None:
Logger.header("⚡ VM TUNING — Swappiness + VFS Cache Pressure")
vm_params = [
("/proc/sys/vm/swappiness", "10"), # prefer RAM, minimal swap
("/proc/sys/vm/vfs_cache_pressure", "50"), # keep dentry/inode longer
("/proc/sys/vm/dirty_ratio", "10"), # writeback threshold
("/proc/sys/vm/dirty_background_ratio", "5"),
]
for path, val in vm_params:
wrote = ADB.syswrite(path, val)
icon = "✓" if wrote else "⚠"
Logger.log(f" {icon} {path.split('/')[-1]} = {val}")
Logger.success("VM params applied")
def deep_clean(self) -> None:
Logger.header("🔄 DEEP CLEAN — Cast-Safe RAM Release")
Logger.log(" am kill-all (releases background apps)...")
ADB.run("am kill-all", silent=True)
Logger.log(" pm trim-caches 2G...")
ADB.run("pm trim-caches 2G", silent=True)
ADB.run("dumpsys batterystats --reset", silent=True)
Logger.log(" drop_caches (requires root)...")
ADB.run_root("sync && echo 3 > /proc/sys/vm/drop_caches")
Logger.shield("Re-verifying Cast services post-clean...")
ChromecastServiceManager.restore()
Logger.success("Deep clean complete — Cast services verified")
# ─────────────────────────────────────────────────────────────────────────────
# NETWORK OPTIMIZER (v12 — full TCP kernel stack)
# ─────────────────────────────────────────────────────────────────────────────
class NetworkOptimizer:
"""
Full TCP/IP stack tuning.
v12 additions:
+ /proc/sys/net/ipv4/tcp_window_scaling, tcp_timestamps, tcp_sack (kernel)
+ BBR congestion control attempt (may not be available on Android 9 STB)
+ /proc/sys/net/core/rmem_max = 16MB
+ wifi.supplicant_scan_interval = 300 (reduce AP scan interference)
+ persist.debug.wfd.enable = 1 (WiFi Direct for Cast fallback)
"""
def apply_tcp(self) -> None:
Logger.header("🌐 NETWORK — TCP/IP Stack + Kernel Params (v12)")
Logger.shield(
"mDNS multicast (Cast discovery port 5353) unaffected by TCP tuning."
)
# Android-level buffers
ADB.setting_put("global", "net.tcp.buffersize.wifi",
"262144,1048576,2097152,131072,524288,1048576")
Logger.log(" ✓ WiFi TCP buffers: 256KB / 1MB / 2MB")
ADB.setting_put("global", "net.tcp.buffersize.ethernet",
"524288,2097152,4194304,262144,1048576,2097152")
Logger.log(" ✓ Ethernet TCP buffers: 512KB / 2MB / 4MB")
ADB.setting_put("global", "tcp_default_init_rwnd", "120")
Logger.log(" ✓ TCP init rwnd = 120 segments")
# Kernel-level TCP params (via root)
kernel_tcp = [
("/proc/sys/net/ipv4/tcp_window_scaling", "1"),
("/proc/sys/net/ipv4/tcp_timestamps", "1"),
("/proc/sys/net/ipv4/tcp_sack", "1"),
("/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"),
("/proc/sys/net/ipv4/tcp_fastopen", "3"),
]
for path, val in kernel_tcp:
wrote = ADB.syswrite(path, val)
icon = "✓" if wrote else "⚠"
Logger.log(f" {icon} {path.split('/')[-1]} = {val}")
# Core net buffers (16MB)
for path in ("/proc/sys/net/core/rmem_max", "/proc/sys/net/core/wmem_max"):
ADB.syswrite(path, "16777216")
Logger.log(" ✓ Net core rmem/wmem_max = 16MB")
# BBR congestion control (best-effort — may not be available)
bbr_path = "/proc/sys/net/ipv4/tcp_congestion_control"
bbr_result = ADB.run_root(f"echo bbr > {bbr_path}")
verify = ADB.run_root(f"cat {bbr_path}").strip()
if "bbr" in verify:
Logger.success(" TCP congestion control: BBR")
else:
Logger.log(f" ⚠ BBR unavailable on this kernel ({verify}) — using CUBIC")
# WiFi stability
ADB.setprop("wifi.supplicant_scan_interval", "300")
Logger.log(" ✓ wifi.supplicant_scan_interval = 300 (reduce interference)")
ADB.setting_put("global", "wifi_sleep_policy", "2")
Logger.log(" ✓ wifi_sleep_policy = 2 (stay connected, Cast-safe)")
# WiFi Direct (Cast fallback transport)
ADB.setprop("persist.debug.wfd.enable", "1")
Logger.log(" ✓ WiFi Direct enabled (Cast fallback transport)")
Logger.success("TCP/IP stack optimized for 4K streaming")
def apply_dns(self, provider: str = "cloudflare") -> None:
DNSManager.set_provider(provider)
# ─────────────────────────────────────────────────────────────────────────────
# HDMI + CEC OPTIMIZER (v12 — BCM Nexus props added)
# ─────────────────────────────────────────────────────────────────────────────
class HDMIOptimizer:
"""
v12 additions vs v11:
+ persist.sys.cec.status = true (BCM CEC HAL enable)
+ persist.sys.hdmi.tx_standby_cec = 1 (CEC standby command)
+ persist.sys.hdmi.tx_view_on_cec = 1 (CEC power-on command)
+ persist.nx.hdmi.tx_standby_cec = 1 (BCM Nexus CEC)
+ persist.nx.hdmi.tx_view_on_cec = 1 (BCM Nexus CEC)
+ persist.sys.hdmi.addr.playback = 11 (CORRECTED: BCM says 11, not 0)
+ persist.nx.vidout.50hz awareness (PAL region)
"""
def apply(self) -> None:
Logger.header("📺 HDMI + CEC — BCM7362 Nexus Full Configuration")
current_cec = ADB.prop("persist.sys.cec.status")
Logger.log(f" Current CEC status: {current_cec}")
hdmi_props = [
# Device type: 4 = playback device (set-top box)
("ro.hdmi.device_type", "4"),
# CORRECTED from v11: BCM Nexus addr.playback = 11 (from pbap9.sh verified)
("persist.sys.hdmi.addr.playback", "11"),
# CEC enable
("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 (persist.nx.* namespace = BCM hardware layer)
("persist.nx.hdmi.tx_standby_cec", "1"),
("persist.nx.hdmi.tx_view_on_cec", "1"),
# Keep HDMI link alive during buffering
("persist.sys.hdmi.keep_awake", "true"),
# HDR10
("persist.sys.hdr.enable", "1"),
# No spurious HDMI resets on hotplug
("ro.hdmi.wake_on_hotplug", "false"),
# ARC / passthrough
("settings put global", "hdmi_cec_enabled", "1"),
]
for entry in hdmi_props:
if len(entry) == 2:
ADB.setprop(entry[0], entry[1])
Logger.log(f" ✓ {entry[0]} = {entry[1]}")
else:
# settings put
ADB.run(f"settings put {entry[0].split()[2]} {entry[1]} {entry[2]}", silent=True)
Logger.log(f" ✓ {entry[1]} = {entry[2]}")
# PAL 50Hz check
hz50 = ADB.prop("persist.nx.vidout.50hz")
if hz50 == "0":
Logger.log(" ℹ 50Hz disabled — enable if PAL content stutters")
else:
Logger.log(f" ✓ 50Hz mode: {hz50}")
Logger.success("HDMI + CEC fully configured with BCM Nexus props")
def fix_audio_desync(self) -> None:
Logger.header("🔊 AUDIO — A/V Sync Fix (HDMI ARC desync elimination)")
audio_props = [
("audio.offload.disable", "1"), # offload causes desync on BCM7362 HDMI
("audio.offload.video", "0"),
("tunnel.audio.encode", "false"),
("audio.deep_buffer.media", "true"), # stable latency baseline
("af.fast_track_multiplier", "1"),
("audio.brcm.hdmi.clock_lock", "true"), # lock to HDMI sink clock
("audio.brcm.hal.latency", "20"),
]
for key, val in audio_props:
ADB.setprop(key, val)
Logger.log(f" ✓ {key} = {val}")
Logger.success("Audio A/V sync fixed — HDMI clock locked")
# ─────────────────────────────────────────────────────────────────────────────
# SYSTEM RESPONSIVENESS (v12 — I/O scheduler, read-ahead)
# ─────────────────────────────────────────────────────────────────────────────
class ResponsivenessOptimizer:
"""
v12 additions:
+ I/O scheduler = deadline (low-latency for eMMC sequential reads)
+ read_ahead_kb = 512 (prefetch for VP9 segment files)
+ CPU governor = performance on both A15 cores
"""
def apply(self, anim_scale: float = 0.5) -> None:
Logger.header(f"🎨 SYSTEM RESPONSIVENESS — Animations + I/O + CPU")
# Animations
for key in ["window_animation_scale", "transition_animation_scale", "animator_duration_scale"]:
ADB.setting_put("global", key, str(anim_scale))
Logger.log(f" ✓ {key} = {anim_scale}x")
# TV recommendations off
for cmd in [
"settings put secure tv_disable_recommendations 1",
"settings put secure tv_enable_preview_programs 0",
"settings put secure tv_watch_next_enabled 0",
]:
ADB.run(cmd, silent=True)
Logger.log(" ✓ TV recommendations disabled")
# Logger reduction (reduce logd overhead)
ADB.setprop("persist.logd.size", "32768")
ADB.setprop("log.tag.stats_log", "OFF")
ADB.setprop("log.tag.statsd", "OFF")
Logger.log(" ✓ Logging reduced to 32KB")
# I/O scheduler: deadline (low latency for streaming segment reads)
io_result = ADB.run_root(
"for d in /sys/block/*/queue/scheduler; do "
"echo deadline > $d 2>/dev/null; done"
)
Logger.log(" ✓ I/O scheduler = deadline (all block devices)")
# read-ahead: 512KB (VP9 segment prefetch)
ADB.run_root(
"for d in /sys/block/*/queue/read_ahead_kb; do "
"echo 512 > $d 2>/dev/null; done"
)
Logger.log(" ✓ 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.run_root(f"echo performance > {path}")
Logger.log(f" ✓ cpu{cpu}: performance governor")
Logger.success("System responsiveness fully optimized")
# ─────────────────────────────────────────────────────────────────────────────
# SAFE DEBLOATER
# ─────────────────────────────────────────────────────────────────────────────
SAFE_DEBLOAT_DB: List[Tuple[str, str]] = [
("com.google.android.backdrop", "Ambient screensaver — idle GPU waste"),
("com.google.android.tvrecommendations", "Content recommendations — constant HTTP polling"),
("com.google.android.katniss", "Google TV voice overlay — high idle CPU"),
("com.google.android.tungsten.setupwraith", "Setup wizard — post-setup dead weight"),
("com.google.android.apps.tv.launcherx", "New Google TV launcher (unused on API 28)"),
("com.google.android.leanbacklauncher", "Legacy Leanback duplicate"),
("com.google.android.marvin.talkback", "Accessibility TTS — 40MB unused"),
("com.google.android.onetimeinitializer","One-time init — already completed"),
("com.google.android.feedback", "Google feedback service — periodic ping"),
("com.google.android.speech.pumpkin", "Hotword detection — high background CPU"),
("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 provider — unused on TV"),
("com.android.providers.contacts", "Contacts provider — unused on TV"),
("com.sagemcom.stb.setupwizard", "Sagemcom factory setup wizard"),
("com.orange.fr.tv.assistance", "Orange assistance — server polling"),
("com.orange.fr.tv.guide", "Orange TV guide — heavy background sync"),
("com.amazon.amazonvideo.livingroom", "Amazon Prime — pre-bundled, use standalone"),
("com.google.android.play.games", "Play Games — unused on TV"),
("com.google.android.videos", "Play Movies — unused on TV"),
("com.google.android.partnersetup", "Partner setup — post-setup unused"),
]
class SafeDebloater:
def run(self) -> None:
Logger.header("🗑 SAFE DEBLOAT — Chromecast Protection Enforced")
disabled, protected, already_off, failed = 0, 0, 0, 0
for pkg, reason in SAFE_DEBLOAT_DB:
if ChromecastProtection.is_protected(pkg):
protected += 1
Logger.shield(f"PROTECTED: {pkg}")
Logger.log(f" └─ {ChromecastProtection.get_reason(pkg)}", "cyan")
continue
if not ADB.pkg_enabled(pkg):
already_off += 1
continue
result = ADB.run(f"pm disable-user --user 0 {pkg}", silent=True)
if "disabled" in result.lower() or result == "":
disabled += 1
Logger.success(f"Disabled: {pkg}")
Logger.log(f" └─ {reason}", "dim")
else:
failed += 1
Logger.warning(f"Could not disable: {pkg}")
Logger.header(
f"DEBLOAT: {disabled} disabled | {protected} protected | "
f"{already_off} already off | {failed} failed"
)
# ─────────────────────────────────────────────────────────────────────────────
# CHROMECAST SERVICE MANAGER
# ─────────────────────────────────────────────────────────────────────────────
class ChromecastServiceManager:
"""
Audit, restore, harden Cast services.
v12: added detection of debloat.sh damage pattern
(apps.mediashell disabled by erroneous debloat script on device).
"""
@staticmethod
def audit() -> Dict[str, bool]:
Logger.header("🔍 CHROMECAST SERVICE AUDIT")
results: Dict[str, bool] = {}
for pkg, reason in ChromecastProtection.PROTECTED.items():
ok = ADB.pkg_enabled(pkg)
results[pkg] = ok
icon = "✓" if ok else "✗"
Logger.log(f" {icon} {pkg}", "success" if ok else "error")
Logger.log(f" └─ {reason}", "cyan")
broken = [p for p, e in results.items() if not e]
if broken:
Logger.warning(f"{len(broken)} Cast service(s) DISABLED — Auto-repair available (option 7)")
else:
Logger.success("All Chromecast services healthy ✓")
return results
@staticmethod
def restore() -> None:
Logger.header("🛡 CHROMECAST RESTORATION")
for pkg in ChromecastProtection.PROTECTED:
ADB.run(f"pm enable {pkg}", silent=True)
ADB.run(f"pm enable --user 0 {pkg}", silent=True)
Logger.shield(f"Ensured: {pkg}")
Logger.success("All Cast services re-enabled")
@staticmethod
def optimize_network() -> None:
Logger.header("📡 CAST NETWORK — mDNS/Multicast Reliability")
ADB.setting_put("global", "wifi_sleep_policy", "2")
ADB.setting_put("global", "wifi_power_save", "0")
ADB.setprop("ro.mdns.enable_passive_mode", "false")
ADB.setprop("net.ssdp.ttl", "4")
Logger.success("Cast mDNS/multicast network profile active")
@staticmethod
def harden_oom() -> None:
Logger.header("🎯 CAST OOM HARDENING")
for pkg in [
"com.google.android.apps.mediashell",
"com.google.android.gms",
"com.google.android.nearby",
]:
pid = ADB.run(f"pidof {pkg}", silent=True).strip()
if pid and pid.isdigit():
ADB.run_root(f"echo 100 > /proc/{pid}/oom_score_adj")
Logger.shield(f"OOM adj=100: {pkg} (PID {pid})")
else:
Logger.log(f" {pkg} not running yet — protected at next start", "warning")
Logger.success("Cast processes hardened")
# ─────────────────────────────────────────────────────────────────────────────
# AOT COMPILER
# ─────────────────────────────────────────────────────────────────────────────
class AOTCompiler:
APPS: Dict[str, str] = {
DeviceSpec.SMARTTUBE_PKG_STABLE: "SmartTube Stable",
DeviceSpec.PROJECTIVY_PKG: "Projectivy Launcher",
"com.google.android.apps.mediashell": "Cast Receiver",
}
@classmethod
def compile_all(cls) -> None:
Logger.header("⚡ AOT COMPILATION — Eliminate JIT Bursts")
for pkg, name in cls.APPS.items():
if not ADB.pkg_installed(pkg):
Logger.log(f" ○ {name} not installed — skip", "dim")
continue
Logger.log(f" Compiling {name} (speed-profile)... ~60–90s")
result = ADB.run(f"cmd package compile -m speed-profile -f {pkg}", silent=True)
if "success" in result.lower():
Logger.success(f" {name}: AOT compiled (speed-profile)")
else:
# fallback
ADB.run(f"cmd package compile -m speed -f {pkg}", silent=True)
Logger.success(f" {name}: AOT compiled (speed)")
# ═════════════════════════════════════════════════════════════════════════════
# SELF-DIAGNOSTIC ENGINE
# ═════════════════════════════════════════════════════════════════════════════
class DiagnosticEngine:
"""
Interactive self-diagnostic system that:
1. Checks 8 service categories comprehensively
2. Presents results category-by-category with interactive drill-down
3. Identifies broken/missing/incorrect settings
4. Offers targeted auto-repair per finding
5. Downloads and reinstalls APKs when needed
Categories:
A. System Health (RAM, CPU, Thermal, Storage)
B. Cast Services (all 10 protected packages)
C. SmartTube (install, version, codec settings, AOT)
D. Video Pipeline (codec props, tunnel, renderer)
E. Network/DNS (TCP params, DNS correctness)
F. Audio (desync props, HDMI clock)
G. Memory/LMK (LMK props, dalvik heap, kernel VM)
H. HDMI/CEC (CEC props, BCM Nexus, 50Hz)
"""
def __init__(self):
self.results: List[DiagResult] = []
def _r(self, cat: str, check: str, status: Status, found: str,
expected: str = "", fix_fn: Optional[Callable] = None,
detail: str = "") -> DiagResult:
dr = DiagResult(cat, check, status, found, expected, fix_fn, detail)
self.results.append(dr)
return dr
# ─────────────────── CATEGORY A: SYSTEM HEALTH ──────────────────────────
def _check_system_health(self) -> List[DiagResult]:
results = []
cat = "SYSTEM"
meminfo = ADB.run("cat /proc/meminfo", silent=True)
fields: Dict[str, int] = {}
for line in meminfo.splitlines():
p = line.split()
if len(p) >= 2 and p[1].isdigit():
fields[p[0].rstrip(":")] = int(p[1])
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
if pct > 30:
s = Status.OK
elif pct > 15:
s = Status.WARN
else:
s = Status.BROKEN
r = self._r(cat, "RAM Available", s, f"{avail_mb} MB ({pct:.0f}%)",
">30% = OK", lambda: MemoryOptimizer().deep_clean(),
f"Total: {total_mb} MB")
results.append(r)
# Thermal
for zone in range(2):
raw = ADB.run(f"cat /sys/class/thermal/thermal_zone{zone}/temp", silent=True)
if raw and raw.lstrip("-").isdigit():
temp_c = int(raw) / 1000
s = Status.OK if temp_c < 60 else (Status.WARN if temp_c < 75 else Status.BROKEN)
r = self._r(cat, f"Thermal zone{zone}", s, f"{temp_c:.1f}°C", "<60°C")
results.append(r)
# Storage
storage = ADB.run("df -h /data", silent=True)
lines = storage.splitlines()
if len(lines) > 1:
parts = lines[1].split()
use_pct_str = parts[4] if len(parts) > 4 else "?"
use_pct = int(use_pct_str.replace("%", "")) if use_pct_str != "?" else 0
s = Status.OK if use_pct < 80 else (Status.WARN if use_pct < 90 else Status.BROKEN)
r = self._r(cat, "/data storage", s, use_pct_str, "<80%")
results.append(r)
# Internet
ping = ADB.run("ping -c 2 -W 3 1.1.1.1", silent=True)
ok = "2 received" in ping
r = self._r(cat, "Internet connectivity", Status.OK if ok else Status.BROKEN,
"Reachable" if ok else "OFFLINE")
results.append(r)
return results
# ─────────────────── CATEGORY B: CAST SERVICES ──────────────────────────
def _check_cast(self) -> List[DiagResult]:
results = []
cat = "CAST"
for pkg, reason in ChromecastProtection.PROTECTED.items():
enabled = ADB.pkg_enabled(pkg)
s = Status.OK if enabled else Status.BROKEN
r = self._r(cat, pkg.split(".")[-1], s,
"enabled" if enabled else "DISABLED",
"enabled", ChromecastServiceManager.restore,
reason)
results.append(r)
return results
# ─────────────────── CATEGORY C: SMARTTUBE ──────────────────────────────
def _check_smarttube(self) -> List[DiagResult]:
results = []
cat = "SMARTTUBE"
# Package detection (check all known names)
found_pkg = None
for pkg in [DeviceSpec.SMARTTUBE_PKG_STABLE, DeviceSpec.SMARTTUBE_PKG_BETA,
DeviceSpec.SMARTTUBE_PKG_LEGACY]:
if ADB.pkg_installed(pkg):
found_pkg = pkg
break
if found_pkg:
ver = ADB.pkg_version(found_pkg)
r = self._r(cat, "SmartTube installed", Status.OK, f"{found_pkg} v{ver}", "installed")
results.append(r)
# AOT compilation status
aot_check = ADB.run(
f"cmd package dump {found_pkg} | grep -i compile_reason", silent=True
)
aot_ok = "install" in aot_check.lower() or "bg-dexopt" in aot_check.lower()
r = self._r(cat, "AOT compilation", Status.OK if aot_ok else Status.WARN,
aot_check[:50] if aot_check else "unknown",
"compiled", AOTCompiler.compile_all)
results.append(r)
else:
r = self._r(
cat, "SmartTube installed", Status.MISSING,
"NOT INSTALLED", "org.smarttube.stable",
lambda: APKDownloader.download_and_install(
DeviceSpec.SMARTTUBE_STABLE_URL,
DeviceSpec.SMARTTUBE_PKG_STABLE,
"SmartTube Stable"
)
)
results.append(r)
# Codec props
for prop, expected in [
("media.vcodec.preferhw", "true"),
("debug.stagefright.ccodec", "1"),
("media.tunneled-playback.enable", "true"),
("media.codec.av1.disable", "true"),
]:
val = ADB.prop(prop)
ok = val == expected
r = self._r(cat, f"prop:{prop.split('.')[-1]}", Status.OK if ok else Status.BROKEN,
val or "not set", expected,
VideoEngineOptimizer().optimize_codec_pipeline)
results.append(r)
return results
# ─────────────────── CATEGORY D: VIDEO PIPELINE ─────────────────────────
def _check_video(self) -> List[DiagResult]:
results = []
cat = "VIDEO"
checks = [
("debug.hwui.renderer", "skiagl"),
("debug.renderengine.backend", "skiaglthreaded"),
("debug.sf.hw", "1"),
("debug.gr.numframebuffers", "3"),
("media.acodec.preferhw", "true"),
("persist.sys.ui.hw", "true"),
("debug.sf.latch_unsignaled", "1"),
]
vo = VideoEngineOptimizer()
for prop, expected in checks:
val = ADB.prop(prop)
ok = val == expected
r = self._r(cat, prop.split(".")[-1], Status.OK if ok else Status.BROKEN,
val or "not set", expected, vo.optimize_rendering)
results.append(r)
return results
# ─────────────────── CATEGORY E: NETWORK / DNS ──────────────────────────
def _check_network(self) -> List[DiagResult]:
results = []
cat = "NETWORK"
# DNS correctness check
dot_host = ADB.setting_get("global", "private_dns_specifier")
dot_mode = ADB.setting_get("global", "private_dns_mode")
ip1 = ADB.prop("net.dns1")
valid_dots = [v[0] for v in DeviceSpec.DNS_PROVIDERS.values()]
dns_ok = dot_host in valid_dots and dot_mode == "hostname"
r = 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: DNSManager.set_provider("cloudflare"),
f"Legacy DNS (net.dns1): {ip1}")
results.append(r)
# Old (incorrect) Cloudflare hostname detection
if dot_host == "dns.cloudflare.com":
r = self._r(cat, "DNS hostname correctness", Status.BROKEN,
"dns.cloudflare.com (WRONG)",
"one.one.one.one",
lambda: DNSManager.set_provider("cloudflare"),
"Legacy hostname — will cause DoT handshake failure")
results.append(r)
# TCP init window
rwnd = ADB.setting_get("global", "tcp_default_init_rwnd")
r = self._r(cat, "TCP init rwnd", Status.OK if rwnd == "120" else Status.WARN,
rwnd or "not set", "120",
NetworkOptimizer().apply_tcp)
results.append(r)
# WiFi sleep policy
sleep = ADB.setting_get("global", "wifi_sleep_policy")
r = self._r(cat, "WiFi sleep policy", Status.OK if sleep == "2" else Status.WARN,
sleep or "not set", "2 (always on)")
results.append(r)
return results
# ─────────────────── CATEGORY F: AUDIO ──────────────────────────────────
def _check_audio(self) -> List[DiagResult]:
results = []
cat = "AUDIO"
ha = HDMIOptimizer()
for prop, expected in [
("audio.offload.disable", "1"),
("audio.deep_buffer.media", "true"),
("audio.brcm.hdmi.clock_lock", "true"),
("tunnel.audio.encode", "false"),
]:
val = ADB.prop(prop)
ok = val == expected
r = self._r(cat, prop.split(".")[-1], Status.OK if ok else Status.BROKEN,
val or "not set", expected, ha.fix_audio_desync)
results.append(r)
return results
# ─────────────────── CATEGORY G: MEMORY ─────────────────────────────────
def _check_memory(self) -> List[DiagResult]:
results = []
cat = "MEMORY"
mo = MemoryOptimizer()
for prop, expected in [
("dalvik.vm.heapsize", DeviceSpec.DALVIK_HEAP_SIZE),
("dalvik.vm.heapgrowthlimit", DeviceSpec.DALVIK_HEAP_GROWTH),
("dalvik.vm.usejit", "true"),
("ro.lmk.kill_heaviest_task", "true"),
("ro.lmk.critical", "0"),
]:
val = ADB.prop(prop)
ok = val == expected
r = self._r(cat, prop.split(".")[-1], Status.OK if ok else Status.BROKEN,
val or "not set", expected, mo.apply_lmk_profile)
results.append(r)
# Swappiness
swap = ADB.run_root("cat /proc/sys/vm/swappiness").strip()
r = self._r(cat, "vm.swappiness", Status.OK if swap == "10" else Status.WARN,
swap or "unknown", "10", mo.apply_vm_tuning)
results.append(r)
return results
# ─────────────────── CATEGORY H: HDMI/CEC ───────────────────────────────
def _check_hdmi(self) -> List[DiagResult]:
results = []
cat = "HDMI"
ho = HDMIOptimizer()
for prop, expected in [
("persist.sys.cec.status", "true"),
("persist.sys.hdmi.addr.playback", "11"), # corrected v12
("persist.sys.hdmi.keep_awake", "true"),
("persist.nx.hdmi.tx_standby_cec", "1"),
("persist.sys.hdr.enable", "1"),
]:
val = ADB.prop(prop)
ok = val == expected
r = self._r(cat, prop.split(".")[-1], Status.OK if ok else Status.WARN,
val or "not set", expected, ho.apply)
results.append(r)
return results
# ─────────────────── RUN CATEGORY ───────────────────────────────────────
CATEGORY_MAP: Dict[str, Tuple[str, Callable]] = {} # filled post-class
def run_category(self, cat_id: str) -> List[DiagResult]:
cat_fn_map = {
"A": ("System Health", self._check_system_health),
"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 = cat_fn_map.get(cat_id.upper())
if not entry:
return []
name, fn = entry
Logger.header(f"🔎 DIAGNOSTICS: {cat_id} — {name}")
results = fn()
self._print_category_results(results)
return results
def _print_category_results(self, results: List[DiagResult]) -> None:
ok_count = sum(1 for r in results if r.is_ok)
bad_count = sum(1 for r in results if r.needs_repair)
warn_count = sum(1 for r in results if r.status == Status.WARN)
for r in results:
if r.status == Status.OK:
Logger.log(f" ✓ [{r.category}] {r.check}: {r.found}", "success")
elif r.status == Status.WARN:
Logger.log(f" ⚠ [{r.category}] {r.check}: {r.found} (expected: {r.expected})", "warning")
elif r.status in (Status.BROKEN, Status.MISSING):
Logger.log(f" ✗ [{r.category}] {r.check}: {r.found} (expected: {r.expected})", "error")
if r.detail:
Logger.log(f" └─ {r.detail}", "dim")
Logger.log(
f"\n Results: {ok_count} OK | {warn_count} WARN | {bad_count} NEEDS REPAIR",
"cyan"
)
def run_all_interactive(self) -> None:
"""Run all 8 categories interactively with repair offer per category."""
all_categories = ["A", "B", "C", "D", "E", "F", "G", "H"]
cat_names = {
"A": "System Health",
"B": "Cast Services (Chromecast)",
"C": "SmartTube",
"D": "Video Pipeline",
"E": "Network / DNS",
"F": "Audio (A/V Sync)",
"G": "Memory / LMK",
"H": "HDMI / CEC",
}
Logger.header("🔎 INTERACTIVE SELF-DIAGNOSTICS — 8 Categories")
all_broken: List[DiagResult] = []
for cat_id in all_categories:
Logger.log(f"\n[{cat_id}] {cat_names[cat_id]}", "cyan")
results = self.run_category(cat_id)
broken = [r for r in results if r.needs_repair]
all_broken.extend(broken)
if broken:
c = Logger.C
Logger.warning(f"{len(broken)} issue(s) found in {cat_names[cat_id]}")
choice = input(
f" {c['warning']}Repair now? [Y/n/s=skip all] > {c['reset']}"
).strip().lower()
if choice == "s":
Logger.log(" Skipping remaining repairs", "warning")
break
if choice in ("", "y", "yes"):
self._repair_category(broken)
else:
Logger.success(f" {cat_names[cat_id]}: All OK")
# Summary
Logger.header("📋 DIAGNOSTIC SUMMARY")
total = len(self.results)
ok_count = sum(1 for r in self.results if r.is_ok)
broken = sum(1 for r in self.results if r.needs_repair)
warn = sum(1 for r in self.results if r.status == Status.WARN)
Logger.log(f" Total checks : {total}")
Logger.success(f" OK : {ok_count}")
Logger.warning(f" Warnings : {warn}")
Logger.error (f" Need repair : {broken}")
if all_broken:
Logger.warning(f"\n Unresolved issues:")
for r in all_broken:
if r.needs_repair:
Logger.log(f" ✗ [{r.category}] {r.check}: {r.found}", "error")
def _repair_category(self, broken: List[DiagResult]) -> None:
"""Execute fix_fn for all broken items in a category."""
applied_fns: set = set()
for r in broken:
if r.fix_fn and id(r.fix_fn) not in applied_fns:
applied_fns.add(id(r.fix_fn))
Logger.repair(f"Auto-repairing: [{r.category}] {r.check}")
try:
r.fix_fn()
except Exception as e:
Logger.error(f"Repair failed: {e}")
def single_category_menu(self) -> None:
"""Menu for selecting which category to diagnose."""
c = Logger.C
cat_map = {
"A": "System Health",
"B": "Cast Services (Chromecast)",
"C": "SmartTube",
"D": "Video Pipeline",
"E": "Network / DNS",
"F": "Audio (A/V Sync)",
"G": "Memory / LMK",
"H": "HDMI / CEC",
"*": "All categories (interactive)",
}
Logger.header("🔎 DIAGNOSTICS — Select Category")
for k, v in cat_map.items():
Logger.log(f" {c['cyan']}{k}{c['reset']}. {v}")
choice = input(f"\n{c['cyan']}Category [A-H or *] > {c['reset']}").strip().upper()
if choice == "*":
self.run_all_interactive()
elif choice in cat_map:
results = self.run_category(choice)
broken = [r for r in results if r.needs_repair]
if broken:
fix = input(f"\n{c['warning']}Auto-repair {len(broken)} issue(s)? [Y/n] > {c['reset']}").strip().lower()
if fix in ("", "y"):
self._repair_category(broken)
else:
Logger.warning("Invalid category")
# ─────────────────────────────────────────────────────────────────────────────
# AUTO REPAIR ENGINE
# ─────────────────────────────────────────────────────────────────────────────
class AutoRepairEngine:
"""
Self-healing repair system.
Detects broken sectors, prompts user, downloads APKs, reinstalls.
"""
REPAIR_REGISTRY: List[Dict[str, Any]] = [
{
"id": "smarttube_missing",
"name": "SmartTube Stable",
"detect": lambda: not ADB.pkg_installed(DeviceSpec.SMARTTUBE_PKG_STABLE),
"repair": lambda: APKDownloader.download_and_install(
DeviceSpec.SMARTTUBE_STABLE_URL,
DeviceSpec.SMARTTUBE_PKG_STABLE,
"SmartTube Stable"
),
},
{
"id": "smarttube_old_package",
"name": "SmartTube old package (com.teamsmart.videomanager.tv)",
"detect": lambda: ADB.pkg_installed("com.teamsmart.videomanager.tv"),
"repair": lambda: (
Logger.warning("Old SmartTube package found. "
"New: org.smarttube.stable"),
APKDownloader.download_and_install(
DeviceSpec.SMARTTUBE_STABLE_URL,
DeviceSpec.SMARTTUBE_PKG_STABLE,
"SmartTube Stable (migration)"
)
),
},
{
"id": "cast_disabled",
"name": "Chromecast Built-in Daemon disabled (mediashell)",
"detect": lambda: not ADB.pkg_enabled("com.google.android.apps.mediashell"),
"repair": ChromecastServiceManager.restore,
},
{
"id": "gms_disabled",
"name": "Google Play Services disabled (GMS / Cast SDK)",
"detect": lambda: not ADB.pkg_enabled("com.google.android.gms"),
"repair": ChromecastServiceManager.restore,
},
{
"id": "wrong_dns",
"name": "DNS incorrectly set (dns.cloudflare.com → should be one.one.one.one)",
"detect": lambda: ADB.setting_get("global", "private_dns_specifier") == "dns.cloudflare.com",
"repair": lambda: DNSManager.set_provider("cloudflare"),
},
{
"id": "dns_not_set",
"name": "Private DNS not configured",
"detect": lambda: ADB.setting_get("global", "private_dns_mode") != "hostname",
"repair": lambda: DNSManager.set_provider("cloudflare"),
},
{
"id": "projectivy_missing",
"name": "Projectivy Launcher missing",
"detect": lambda: not ADB.pkg_installed(DeviceSpec.PROJECTIVY_PKG),
"repair": lambda: APKDownloader.download_and_install(
DeviceSpec.PROJECTIVY_URL,
DeviceSpec.PROJECTIVY_PKG,
"Projectivy Launcher"
),
},
{
"id": "shizuku_missing",
"name": "Shizuku not installed",
"detect": lambda: not ADB.pkg_installed(DeviceSpec.SHIZUKU_PKG),
"repair": lambda: APKDownloader.download_and_install(
DeviceSpec.SHIZUKU_URL,
DeviceSpec.SHIZUKU_PKG,
"Shizuku"
),
},
{
"id": "av1_not_suppressed",
"name": "AV1 SW decoder active (will cause stuttering)",
"detect": lambda: ADB.prop("media.codec.av1.disable") != "true",
"repair": VideoEngineOptimizer().suppress_av1,
},
{
"id": "wrong_hdmi_addr",
"name": "HDMI addr.playback=0 (should be 11 for BCM Nexus)",
"detect": lambda: ADB.prop("persist.sys.hdmi.addr.playback") not in ("11", ""),
"repair": HDMIOptimizer().apply,
},
{
"id": "audio_desync",
"name": "Audio offload enabled (causes A/V desync on BCM7362 HDMI)",
"detect": lambda: ADB.prop("audio.offload.disable") != "1",
"repair": HDMIOptimizer().fix_audio_desync,
},
]
@classmethod
def scan_and_repair_interactive(cls) -> None:
Logger.header("🔧 AUTO-REPAIR ENGINE — Sector Scan")
Logger.log("Scanning all repair sectors...")
found: List[Dict[str, Any]] = []
for entry in cls.REPAIR_REGISTRY:
try:
detected = entry["detect"]()
except Exception:
detected = False
if detected:
found.append(entry)
Logger.error(f" ✗ BROKEN: {entry['name']}")
else:
Logger.log(f" ✓ OK: {entry['name']}", "dim")
if not found:
Logger.success("All sectors healthy — no repairs needed")
return
Logger.warning(f"\n{len(found)} broken sector(s) detected:")
for i, entry in enumerate(found, 1):
Logger.log(f" {i}. {entry['name']}", "error")
c = Logger.C
choice = input(
f"\n{c['warning']}Repair all {len(found)} sectors? [Y=all / n=select / x=cancel] > {c['reset']}"
).strip().lower()
if choice == "x":
Logger.log("Repair cancelled")
return
if choice == "n":
# Selective repair
for i, entry in enumerate(found, 1):
Logger.log(f"\n[{i}] {entry['name']}")
sub = input(f" {c['warning']}Repair this sector? [Y/n] > {c['reset']}").strip().lower()
if sub in ("", "y"):
cls._apply_repair(entry)
else:
# Repair all
for entry in found:
cls._apply_repair(entry)
Logger.success("Auto-repair complete")
@classmethod
def _apply_repair(cls, entry: Dict[str, Any]) -> None:
Logger.repair(f"Repairing: {entry['name']}")
try:
entry["repair"]()
except Exception as exc:
Logger.error(f"Repair error: {exc}")
# ─────────────────────────────────────────────────────────────────────────────
# SHIZUKU MANAGER
# ─────────────────────────────────────────────────────────────────────────────
class ShizukuManager:
CLASSPATH_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 &"
)
def deploy(self) -> None:
Logger.header("🔑 SHIZUKU — Privilege Engine")
pkg = DeviceSpec.SHIZUKU_PKG
if not ADB.pkg_installed(pkg):
apk = CACHE_DIR / "shizuku.apk"
APKDownloader.download(DeviceSpec.SHIZUKU_URL, apk)
APKDownloader.install(apk, "Shizuku")
else:
Logger.success("Shizuku already installed")
Logger.log("Starting Shizuku bootstrap...")
ADB.run(self.CLASSPATH_CMD)
time.sleep(3)
Logger.success("Shizuku server started")
# ─────────────────────────────────────────────────────────────────────────────
# MAIN ORCHESTRATOR
# ─────────────────────────────────────────────────────────────────────────────
class PlayboxULTRA:
def __init__(self, device: str) -> None:
self.device = device
self.video = VideoEngineOptimizer()
self.memory = MemoryOptimizer()
self.network = NetworkOptimizer()
self.hdmi = HDMIOptimizer()
self.resp = ResponsivenessOptimizer()
self.debloat = SafeDebloater()
self.cast = ChromecastServiceManager()
self.aot = AOTCompiler()
self.diag = DiagnosticEngine()
self.repair = AutoRepairEngine()
self.shizuku = ShizukuManager()
def _banner(self) -> None:
c = Logger.C
print(f"""
{c['header']}{c['bold']}╔══════════════════════════════════════════════════════════════════════╗
║ PLAYBOX TITANIUM v{VERSION}
║ Sagemcom DCTIW362P │ Android TV 9 │ Broadcom BCM7362
╠══════════════════════════════════════════════════════════════════════╣
║ 🎬 SmartTube VP9 HW 🚇 Tunnel Mode 🛡 Cast Protected
║ 🔧 Self-Diagnostic 🩺 Auto-Repair 🔒 DNS: one.one.one.one
╚══════════════════════════════════════════════════════════════════════╝{c['reset']}
Connected: {c['cyan']}{self.device}{c['reset']}
""")
def _menu(self) -> None:
c = Logger.C
while True:
os.system("clear")
self._banner()
print(f"""{c['bold']}╔══════════════════════════════════════════════════════════════════╗{c['reset']}
{c['success']}VIDEO PERFORMANCE{c['reset']}
{c['success']}1.{c['reset']} Codec Pipeline (C2+OMX, ccodec, VP9 HW)
{c['success']}2.{c['reset']} Rendering Engine (skiaglthreaded + VideoCore)
{c['success']}3.{c['reset']} Tunnel Mode (VPU→Display direct)
{c['success']}4.{c['reset']} AV1 Suppression + Dalvik Heap
{c['success']}5.{c['reset']} AOT Compile (SmartTube + Projectivy + Cast)
{c['header']}CHROMECAST PROTECTION{c['reset']}
{c['success']}6.{c['reset']} 🛡 Audit Cast Services
{c['success']}7.{c['reset']} 🛡 Restore Cast Services (emergency)
{c['success']}8.{c['reset']} 📡 Cast mDNS Network Tuning
{c['success']}9.{c['reset']} 🎯 Cast OOM Hardening
{c['success']}P.{c['reset']} 🔍 Show Protection Registry
{c['info']}DIAGNOSTICS & REPAIR{c['reset']}
{c['info']}D.{c['reset']} 🔎 Interactive Self-Diagnostics (8 categories)
{c['info']}R.{c['reset']} 🔧 Auto-Repair Engine (scan + fix all sectors)
{c['info']}N.{c['reset']} 🔒 DNS Manager (interactive selector)
{c['warning']}SYSTEM{c['reset']}
{c['warning']}10.{c['reset']} Network TCP + DNS (Cloudflare auto)
{c['warning']}11.{c['reset']} HDMI + CEC (BCM Nexus props, addr=11)
{c['warning']}12.{c['reset']} Audio A/V Sync Fix
{c['warning']}13.{c['reset']} Memory: LMK + VM + Heap
{c['warning']}14.{c['reset']} System Responsiveness + I/O + CPU
{c['warning']}15.{c['reset']} Safe Debloat (Cast PROTECTED)
{c['warning']}16.{c['reset']} Deep Clean RAM
{c['warning']}17.{c['reset']} Deploy Shizuku
{c['cyan']}AUTO MODES{c['reset']}
{c['cyan']}20.{c['reset']} 🚀 SMARTTUBE ULTRA (video-focused)
{c['cyan']}21.{c['reset']} 🏆 FULL SYSTEM ULTRA (all modules)
{c['error']}0.{c['reset']} Exit{c['bold']}
╚══════════════════════════════════════════════════════════════════════╝{c['reset']}""")
choice = input(f"\n{c['cyan']}Choice > {c['reset']}").strip().lower()
dispatch: Dict[str, Callable] = {
"1": self.video.optimize_codec_pipeline,
"2": self.video.optimize_rendering,
"3": self.video.force_tunnel_mode,
"4": self._codec_and_heap,
"5": self.aot.compile_all,
"6": self.cast.audit,
"7": self.cast.restore,
"8": self.cast.optimize_network,
"9": self.cast.harden_oom,
"p": ChromecastProtection.print_all,
"d": self.diag.single_category_menu,
"r": self.repair.scan_and_repair_interactive,
"n": DNSManager.interactive_select,
"10": self._network_full,
"11": self.hdmi.apply,
"12": self.hdmi.fix_audio_desync,
"13": self._memory_full,
"14": self.resp.apply,
"15": self.debloat.run,
"16": self.memory.deep_clean,
"17": self.shizuku.deploy,
"20": self.run_smarttube_ultra,
"21": self.run_full_ultra,
"0": self._exit,
}
fn = dispatch.get(choice)
if fn:
fn()
else:
Logger.warning("Invalid choice")
if choice != "0":
input(f"\n{c['cyan']}Press Enter...{c['reset']}")
def _codec_and_heap(self):
self.video.suppress_av1()
self.memory.apply_dalvik_heap()
def _network_full(self):
self.network.apply_tcp()
self.network.apply_dns("cloudflare")
def _memory_full(self):
self.memory.apply_lmk_profile()
self.memory.apply_vm_tuning()
self.memory.apply_dalvik_heap()
def _exit(self) -> None:
Logger.save()
Logger.log(f"Log: {LOG_FILE}", "cyan")
sys.exit(0)
# ── SmartTube Ultra ──────────────────────────────────────────────────────
def run_smarttube_ultra(self) -> None:
Logger.header("🚀 SMARTTUBE ULTRA — Maximum Video Performance")
pipeline: List[Tuple[str, Callable]] = [
("Auto-Repair pre-check", self.repair.scan_and_repair_interactive),
("Chromecast Audit", self.cast.audit),
("Codec Pipeline (C2+OMX)", self.video.optimize_codec_pipeline),
("Rendering (skiaglthreaded)", self.video.optimize_rendering),
("Tunnel Mode", self.video.force_tunnel_mode),
("AV1 Suppression + Dalvik Heap", self._codec_and_heap),
("Audio A/V Sync", self.hdmi.fix_audio_desync),
("HDMI + CEC (BCM Nexus)", self.hdmi.apply),
("Memory: LMK + VM + Heap", self._memory_full),
("Responsiveness + I/O + CPU", self.resp.apply),
("TCP Network + DNS (Cloudflare)", self._network_full),
("Cast mDNS Tuning", self.cast.optimize_network),
("Cast OOM Hardening", self.cast.harden_oom),
("AOT Compilation", self.aot.compile_all),
("Cast Services Restore (final)", self.cast.restore),
]
total = len(pipeline)
for i, (name, fn) in enumerate(pipeline, 1):
Logger.log(f"\n[{i}/{total}] {name}...", "cyan")
fn()
time.sleep(0.3)
Logger.header("🎉 SMARTTUBE ULTRA COMPLETE")
Logger.success("VP9 HW + Tunnel Mode + AOT + DNS: one.one.one.one + Cast Protected ✓")
Logger.log("SmartTube: Settings → Player → Video codec → VP9", "warning")
Logger.save()
# ── Full System Ultra ────────────────────────────────────────────────────
def run_full_ultra(self) -> None:
Logger.header("🏆 FULL SYSTEM ULTRA — Complete Optimization")
pipeline: List[Tuple[str, Callable]] = [
("System Diagnostics (baseline)", lambda: self.diag.run_category("A")),
("Auto-Repair pre-check", self.repair.scan_and_repair_interactive),
("Chromecast Audit", self.cast.audit),
("Codec Pipeline (C2+OMX)", self.video.optimize_codec_pipeline),
("Rendering (skiaglthreaded)", self.video.optimize_rendering),
("Tunnel Mode", self.video.force_tunnel_mode),
("AV1 Suppression + Heap", self._codec_and_heap),
("Audio A/V Sync", self.hdmi.fix_audio_desync),
("HDMI + CEC (BCM Nexus addr=11)", self.hdmi.apply),
("TCP + DNS (Cloudflare DoT)", self._network_full),
("Memory: LMK + VM + Heap", self._memory_full),
("Responsiveness + I/O + CPU", self.resp.apply),
("Safe Debloat (Cast Protected)", self.debloat.run),
("Cast mDNS Network", self.cast.optimize_network),
("Cast OOM Hardening", self.cast.harden_oom),
("AOT Compilation", self.aot.compile_all),
("Deep Clean RAM", self.memory.deep_clean),
("Final Cast Audit", self.cast.audit),
]
total = len(pipeline)
for i, (name, fn) in enumerate(pipeline, 1):
Logger.log(f"\n[{i}/{total}] {name}...", "cyan")
fn()
time.sleep(0.2)
Logger.header("🏆 FULL SYSTEM ULTRA COMPLETE")
Logger.success("All modules applied. Chromecast: PROTECTED. DNS: FIXED. Cast: SECURED.")
Logger.log(f"Reboot: adb -s {self.device} reboot", "warning")
Logger.save()
# ─────────────────────────────────────────────────────────────────────────────
# CLI ENTRY POINT
# ─────────────────────────────────────────────────────────────────────────────
def _parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(
description=f"Playbox Titanium v{VERSION} — Sagemcom DCTIW362P",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
EXAMPLES:
python3 Autopilot_12_ULTRA.py # Interactive menu
python3 Autopilot_12_ULTRA.py --smarttube-ultra # Video-focused ultra
python3 Autopilot_12_ULTRA.py --full-ultra # All modules
python3 Autopilot_12_ULTRA.py --diag # Self-diagnostics
python3 Autopilot_12_ULTRA.py --repair # Auto-repair scan
python3 Autopilot_12_ULTRA.py --cast-restore # Emergency Cast fix
python3 Autopilot_12_ULTRA.py --dns cloudflare # Fix DNS
python3 Autopilot_12_ULTRA.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", help="Run interactive self-diagnostics")
p.add_argument("--repair", action="store_true", help="Run auto-repair engine")
p.add_argument("--cast-audit", action="store_true")
p.add_argument("--cast-restore", action="store_true")
p.add_argument("--dns", default=None, metavar="PROVIDER",
help="Set DNS: cloudflare|google|quad9|adguard|nextdns")
p.add_argument("--beta", action="store_true")
return p
def main() -> None:
args = _parser().parse_args()
device = args.device or ADB.detect() or DEFAULT_ADB_HOST
if args.device:
Logger.log(f"Device: {device} (specified)")
elif ADB.detect():
Logger.success(f"Auto-detected: {device}")
else:
Logger.warning(f"No device detected — using default: {device}")
if not ADB.connect(device):
Logger.error(f"Cannot connect to {device}")
sys.exit(1)
app = PlayboxULTRA(device)
if args.cast_restore:
ChromecastServiceManager.restore()
elif args.cast_audit:
ChromecastServiceManager.audit()
elif args.dns:
DNSManager.set_provider(args.dns)
elif args.diag:
app.diag.run_all_interactive()
elif args.repair:
app.repair.scan_and_repair_interactive()
elif args.smarttube_ultra:
app.run_smarttube_ultra()
elif args.full_ultra:
app.run_full_ultra()
else:
app._banner()
app._menu()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print()
Logger.warning("Interrupted (Ctrl+C)")
Logger.save()
sys.exit(0)
except Exception as exc:
Logger.error(f"Unexpected error: {exc}")
import traceback
traceback.print_exc()
sys.exit(1)
#!/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.0.0 | Autor: Senior Android/WearOS Eng. ║
# ╚══════════════════════════════════════════════════════════════════╝
# 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.0.0"
readonly SCRIPT_DATE="2026-01-20"
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"
}
# ═══════════════════════════════════════════════════════════════════
# 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_total=$(adb_get "grep MemTotal /proc/meminfo | awk '{print \$2}'")
mem_avail=$(adb_get "grep MemAvailable /proc/meminfo | awk '{print \$2}'")
mem_free=$(adb_get "grep MemFree /proc/meminfo | awk '{print \$2}'")
local mem_used=$(( (mem_total - mem_avail) / 1024 ))
local mem_total_mb=$(( mem_total / 1024 ))
local mem_avail_mb=$(( mem_avail / 1024 ))
status_row "RAM łącznie" "${mem_total_mb} MB"
status_row "RAM dostępna" "${mem_avail_mb} MB"
status_row "RAM użyta" "${mem_used} MB"
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"
status_row "Klatki z jankiem (>16ms)" "${janky_frames:-?}"
local jank_pct=$
#!/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