Files
cleanyourtv/docs/TROUBLESHOOTING.md

174 KiB
Raw Permalink Blame History

Troubleshooting

Things that bit us or contributors, so they don't bite you. Commands use tools/tv.py; see the README for setting it up.

Connecting

  • macOS: "No route to host" (EHOSTUNREACH) although the port is open. Recent macOS keeps a process off the local subnet under Local Network privacy, and the kernel reports that as this error, the same one a TV that is switched off gives. tools/tv.py does not avoid it: the permission belongs to the app that runs the client (Terminal, iTerm2, VS Code), not to the adb binary or to python, so changing ADB client makes no difference. Allow that app under System Settings > Privacy & Security > Local Network, then quit and reopen it; a denial sticks once it has happened, and a prompt with no window to appear in is denied for you. Confirm it before changing anything on the network, with the default gateway: in practice it keeps answering while every other address on the subnet gives this error, so if the router connects and nothing else does, it is the permission and not the network. ARP and mDNS keep working throughout, which is what makes it read as AP isolation or a router firewall: arp -an shows the TV's MAC and dns-sd -B _androidtvremote2._tcp still finds it by name while no port on it can be opened. Seen on macOS 26 (Darwin 25.6) against a Hisense hengshan set, Google TV 12, where the stock adb and tools/tv.py failed identically. The closing check is denied the same way, and it is the one the procedure accepts: tools/adb-closed.py <tv ip> answers could not check <ip>:5555 ([Errno 65] No route to host). Is the TV on and on this network? followed by the Local Network note, exit 2, so a machine without the permission can neither confirm nor deny that the port is closed. Grant it to the terminal, reopen it, and check again: only a "closed" from a granted terminal counts. If tools/tv.py reaches the TV from that terminal but the stock adb still answers No route to host (65), the terminal holds the permission and the server adb leaves behind does not, so granting it again cannot help: tools/native-adb.md has a loopback relay for that case (#3336).
  • No "wireless debugging" toggle on some TVs, Xiaomi among them. USB debugging alone opens network ADB on port 5555.
  • USB debugging is greyed out and there is no Wireless debugging entry either, seen on a carrier box (Airtel Xstream, Android 11). Some ISP firmware removes both switches, and then no shell can be reached: tools/tv.py fails, adb connect <ip>:5555 times out, and so does a port check. That timeout is the firmware, not Wi-Fi, the router or the cable, so don't go rebuilding the network for it. Greyed-out rather than missing points at a device-owner policy or a provisioning lock; the toggle comes back after a firmware update from the ISP, or not at all. Without one of the two switches this repo has nothing to talk to.
  • Developer options is hidden, or Build number asks for a password, seen on an ISP box (FPT Play 650, Android TV 14): tapping Build seven times opens a password prompt instead of unlocking the menu. Often only the way in is locked, not the screen itself: TvSettings still has com.android.tv.settings/.system.development.DevelopmentActivity, and any app can open it by its name or with the android.settings.APPLICATION_DEVELOPMENT_SETTINGS action. Install an app that opens activities by name (Activity Launcher) from the Play Store, open that screen and switch on USB debugging there. When no app can open it either, see the carrier-box bullet above.
  • USB debugging never opens port 5555 on some Google TV sets (a Skyworth-built Philips among them). Pair once through Wireless debugging with the stock adb (adb pair <ip>:<port> <code>, then adb connect), run adb tcpip 5555, and tools/tv.py works from then on. Keep 5555 closed if you would rather not open it, since the pairing is enough on its own: with TV_ADB_BINARY naming that same platform-tools adb and TV_HOST naming the Wireless debugging connection port, the tools reach the TV through the paired transport, nothing listens on 5555 and there is no adb tcpip to run again (tools/native-adb.md); read that port again when Wireless debugging is toggled, since it changes. dns-sd -B _adb-tls-pairing._tcp (or adb mdns services) shows the pairing port while the code is on screen. Android switches Wireless debugging off whenever Wi-Fi drops; turn it back on and adb reconnects without a new code. Port 5555 from adb tcpip usually lasts only until the next reboot. When you close up, switch Wireless debugging off on its own: on a Chromecast with Google TV 4K (sabrina) turning USB debugging off left the Wireless debugging port accepting connections, and tools/adb-closed.py checks 5555 unless it is given that port. adb mdns services lists the port as _adb-tls-connect._tcp while it is open: note it before switching Wireless debugging off, and run tools/adb-closed.py <tv ip> <port> straight after, before the reboot. After the reboot the old number reads closed either way, because Wireless debugging takes a new port each time it starts.
  • After adb_enabled was switched off from the shell, the menu says USB debugging is on and port 5555 stays closed. On an Aurzen BOOM Air projector (BOOMAIR, Android 11, RTMA.250416.043) settings put global adb_enabled 0 had closed the port as it should, and when ADB was wanted again Developer options showed USB debugging as on, through a restart, while tools/adb-closed.py <tv ip> kept answering closed. The port came back only after USB debugging was switched off and on again in that menu and the projector was restarted once more. A menu that reads on after the flag was cleared from the shell is therefore no evidence that the port is open: check with tools/adb-closed.py <tv ip> before and after cycling the toggle, and trust what the TV answers rather than the screen. Do that first; the bullet below is for a set whose firmware opens no port at all.
  • Port 5555 stays closed and there is no Wireless debugging entry either, so the pair-once recipe above has nothing to pair with: a Philips 58PUS8505/62 The One, Android TV 12, STT2.230526.001 reported USB debugging on, no Wireless debugging screen, adb connect <tv ip>:5555 refused, and a USB cable to the computer that got no connection either. Two routes are left, and the firmware has to offer one of them. The first is the port-scan bullet further down this section: some firmware runs adbd on a port of its own, so scan and try each open one with TV_PORT=<port> tools/tv.py --probe. The second is the cable, when adb devices lists the set while USB debugging is on: adb tcpip 5555 with the stock adb then opens the port, the cable comes out, and tools/tv.py reaches the TV over the network. The cable is only a way to open the port, never a way to run these tools: tools/tv.py connects with AdbDeviceTcp(HOST, PORT) - <tv ip>:5555, or TV_PORT for another port - and carries no USB transport, so no script here talks to a TV over USB. A set that neither enumerates over USB nor runs adbd on any TCP port has no way in, and this repo does not try to get around that.
  • ping answers, port 5555 refuses, and there is no Wireless debugging entry to pair with either, reported on a Panasonic GX655, Android 9, PTT1.181130.001 with the TV on Ethernet and the computer on Wi-Fi: those two sit on one LAN, and only a guest network or a separate VLAN keeps them apart (the VLAN bullet below), so this is a TV that was reached with no ADB port, not a network fault. The tools say which of the two it is: a refusal prints <tv ip>:5555 refused the connection: nothing listens for ADB there, a set that cannot be reached prints could not reach, and tools/adb-closed.py <tv ip> answers it in its exit status - 0 the TV itself refused, 1 the port is open, 2 it could not tell.
  • A cable to the computer gives no device in adb devices: a TV's USB-A ports are usually host ports, for a drive, a keyboard or an Ethernet adapter, and the cable route in the bullet above needs one that carries ADB device mode instead - the exception, as on the Xiaomi boxes below. On the GX655 the cable gave no device either, and the port scan above is the remaining check; with neither route a firmware offers no way in, and there is nothing to disable on it. Every step here goes through the ADB port, so a set is unchanged by these checks and no list is applied until tools/tv.py --probe answers.
  • USB devices go dead while USB debugging is on, seen on the Xiaomi Mi Box S and the Xiaomi TV Box S 3rd gen (MiTV-AFMU0, Google TV 14): their firmware switches the USB port to ADB device mode, which cuts the power to a USB speaker or drive. A USB Ethernet adapter sits on that same port, so the network goes with it and the box falls back to Wi-Fi without saying so. dumpsys usb | grep host_connected prints false; dumpsys connectivity | grep -o 'Transports: [A-Z]*' prints WIFI on a set the owner calls wired. A bare eth0 stuck at NO-CARRIER is these boards' own socketless MAC, not the adapter - on the TCL 65C735 the adapter came up as eth1. How long the port survives debugging differs by firmware: the two Xiaomi boxes lose it as soon as debugging is on, while the 65C735 kept the adapter until a reboot with debugging still on put the port in gadget mode (sys.usb.config=adb, Mstar-udc) and it never came back (that list's header). Either way there is no way back from the shell on a set with no Wireless debugging, and switching USB debugging off stops adbd too, so plan one sitting rather than two: switch debugging off at the end, replug the adapter, and confirm Ethernet before handing the TV back. The launcher never needs debugging. A Chromecast with Google TV 4K (sabrina, UTTC.250917.004) kept its USB Ethernet adapter with USB debugging on: eth0 stayed listed, and Ethernet connected with debugging still on. It printed host_connected=false while the adapter was present as eth0, so there host_connected=false was no proof the adapter was gone (its list). The port can also stop enumerating anything at all, not just lose its power: on a Xiaomi TV A Pro 55" 2026 (MiTV-MZTU1, MediaTek MT5873, Android TV 14, UKN1.240806.017.V816.0.26.6.8.3.UZTMBTV) no keyboard, flash drive or mouse came up while debugging was on, /dev/bus/usb never gained a device, and /dev/block/sd* stayed empty. Its /vendor/etc/init/hw/init.common.usb.rc loads the dual-role mtu3 controller as a gadget (mtu3_dev_en=1) whenever persist.sys.usb.config=adb, and as host (mtu3_dev_en=0) only at none - the kernel log shows [Vbus] port0 ... [off] - so on that board ADB and a USB host port cannot both be on. Setting the property from the shell is refused (avc: denied { set } for property=sys.usb.config ... tclass=property_service), and re-enabling every package that set's list had disabled left the port dead, so the firmware is the cause, not the list (#3368).
  • Port 5555 refuses the connection although network debugging is on, and no Wireless debugging is advertised, seen on a Wanbo T6R Max projector (MediaTek MT5862, Android 9, TVOS-04.16.031.01.12): the firmware starts adbd on its own port, 5877 there (service.adb.tcp.port), and the TV never shows it. Scan the TV's TCP ports (nmap -p- <tv ip>, or a short Python loop over socket.connect_ex; skip nc on macOS) and try each open port with TV_HOST=<tv ip> TV_PORT=<port> tools/tv.py --probe: the ADB one answers, the others time out. A sandboxed shell can make nmap's own ping-discovery probe fail outright ("Network is down", SO_ERROR 50) before it scans anything; nmap -sT -Pn (a plain TCP connect scan, no ping, no raw sockets) or the Python loop above still works there. Every script here honours TV_PORT, and the closing check is tools/adb-closed.py <tv ip> <port>.
  • Close port 5555 when you're done: plaintext, and a shell for anyone on the LAN when ro.adb.secure=0, or when it is unset on a debug build or Android 4.4 (tools/disable.sh warns about both). Steps in the README FAQ.
  • The TV sits on a separate VLAN or IoT network and tools/tv.py times out although debugging is on: the router keeps the computer from reaching it. Add a temporary firewall rule from your computer to the TV on tcp/5555 only, and remove it again when you close ADB. Opening the whole segment, or moving the TV onto the main network for good, undoes the reason it was separate.
  • Nothing on the network answers while a VPN runs on the computer, not even the router: ping prints General failure on Windows and port 5555 times out, although the TV is on the same Wi-Fi and arp -a lists it. Seen with Proton VPN's free plan on Windows 11, working on a Sankey SMART_TV (CVTE, HiSilicon Hi3751V350, Android 12). The VPN client's firewall blocks the local network; marking the Wi-Fi as a private network does not change that. Turn on the client's local-network option and reconnect: Windscribe has Allow LAN traffic (Preferences > Connection) on its free plan, Proton VPN's Allow LAN connections is for paid plans only. Or disconnect the VPN for the session. Turn the option off again afterwards if you use public Wi-Fi.
  • tools/tv.py times out after the TV took the connection (ADB did not finish the handshake, TcpTimeoutException: Reading from <tv ip>:5555 timed out) and no "Allow USB debugging?" prompt is waiting: another ADB client may be holding the TV, as some firmware's adbd serves one client at a time. Stop that client for the session, then run tools/tv.py --probe again. The stock adb server is one: if adb devices lists the TV, run adb kill-server first. A Xiaomi Mi TV 4A, Android 6.0.1 timed out whenever that server held a connection to the same port. Home Assistant's Android Debug Bridge integration is another (its Android TV Remote integration does not use ADB): a TCL 55C61B, Google TV 12 owner had to disable it in Home Assistant first, because it was holding the TV's ADB connection.
  • Windows PowerShell 5.1 adds a byte-order mark to text piped into a program, which turned the first batch command into gibberish. tv.py now strips it, and the .ps1 scripts pipe clean UTF-8.
  • "Allow USB debugging?" comes back, or the TV answers unauthorized although adb devices already works: tv.py keeps a key of its own in tools/adbkey/ and offers the stock ~/.android/adbkey (#105) after it, so a set that trusts either key connects with no prompt. Older copies offered the stock key only while tools/adbkey did not exist, which kept a TCL 55C61B unauthorized (#1526), so update the checkout first. When the TV trusts neither key, the prompt asks for tv.py's own. To have it register the stock key instead, so adb and tv.py share one approval, delete the folder: rm -rf tools/adbkey (Remove-Item -Recurse -Force tools\adbkey in PowerShell, rmdir /s /q tools\adbkey in Command Prompt), then tools/tv.py --probe (tools\venv\Scripts\python.exe -I tools\tv.py --probe on Windows) and accept that one prompt. Every attempt with an unaccepted key queues one more prompt on the TV, so dismiss them all (Back) before testing with key presses.
  • Git Bash rewrites paths like /data/local/tmp into Windows paths. The scripts set MSYS_NO_PATHCONV=1 for you. Set JAVA_HOME and ANDROID_HOME before building there.
  • pip install -r tools/requirements.txt fails on Windows on ARM with Failed building wheel for cryptography (#2855). The ARM64 build of Python reports win_arm64, PyPI's newest win_arm64 wheel for cryptography is 46.0.3, and the file asks for >=46.0.7, so pip falls back to the source tarball, which needs Rust and OpenSSL. Nothing about the TV is involved, and the way through is on the PC: make tools\venv with the x64 build of Python from python.org instead. Windows 11 on ARM runs it, and 46.0.7 has a win_amd64 wheel. Whether that floor can come down is #2855's question for the maintainer, not a step in a cleanup.

Working on the TV

  • Chained pm commands (pm disable a; pm disable b) may silently stop after the first. Run one per shell call; --batch does exactly that.

  • YouTube Kids opens a "YouTube Kids has moved" notice instead of videos. The standalone com.google.android.youtube.tvkids app is showing its migration notice; that screen alone is not evidence of a launcher or debloat fault. Google's TV instructions now use a YouTube Kids profile inside the regular YouTube app. Keep YouTube enabled, leave the notice with Home and open YouTube. Choose the kid's profile on "Who's Watching?", or open the profile picture in the left-hand menu if YouTube is already signed in. If no Kids profile exists, follow Google's setup instructions; the owner should configure the profile and parental controls on the TV. If the owner wants the obsolete Home shortcut hidden, remove com.google.android.youtube.tvkids from dock_apps and add it to hidden_apps in config.xml, then rebuild and reinstall with the existing launcher installer. An active theme can override those same keys. This hides Home tiles without disabling the package or clearing app data; it is not a parental-control restriction. Undo by removing it from hidden_apps and restoring its dock entry, then rebuilding (or restoring the theme's keys). Seen on Xiaomi MiTV-AXSO0 (dangal, Android TV 9, PI.6036), with the Kids package still enabled.

  • TCL's software update says "Local network or server abnormal" after the debloat, while the network works. TCL's updater gets the TV's device number (dnum) from com.tcl.xian.StartandroidService, which the lists disable as reporting; without it the check stops with dnum is null! in logcat. Enable it, reboot once (it starts at boot), run Settings > About > Software Update > Network Update, then disable it again. Seen on a TCL Smart TV Pro (G03_4K_US, Google TV 11, GTV93).

  • The Google Home panel (lights) is gone from the quick settings on Google TV. It lives in com.google.android.apps.tv.dreamx, the same package as the promo screensaver, and Apps only mode hides it too. Re-enable dreamx and switch Apps only mode off to bring it back; the screensaver can stay on the launcher's clock. Seen on a TCL Smart TV Pro (G03_4K_US, Google TV 11, GTV93).

  • The Google Home panel opens and closes at once, though dreamx is still enabled, once the stock launcher is off: dreamx (com.google.android.apps.tv.dreamx) loads the panel's account and the device trust through launcherx, so with com.google.android.apps.tv.launcherx disabled logcat shows Failed to find provider info for com.google.android.apps.tv.launcherx.coreservices.account.AccountModeContentProvider, then EligibilityManagerImpl: SmartHome: Error loading account, and a bindService to launcherx's TrustedNetworkStateService returning false. Re-enabling launcherx brings the panel back straight away, with no reboot. Sparing launcherx's home activity alone is refused as well (Shell cannot change component state, as in the bullet on pm disable <package>/<class> below), so it is the whole package or the panel: if the household uses Google Home lights, keep launcherx enabled and hand Home to the new launcher with stock_home_redirect (themes/README.md) instead of disabling it. Seen on a TCL Smart TV Pro (G03_4K_US, Google TV 11, GTV93, #1653).

  • Once launcherx is enabled again, Apps offers no Disable for it, so Home cannot be handed back without ADB (TCL Smart TV Pro, G10_4K_GB, Android 14, UTT2.250416.001). Settings > Apps > See all apps > Google TV shows Enable while the package is disabled, and once it is enabled the same screen offers only Force stop and Uninstall updates: there is no Disable to press, and Uninstall updates reverts launcherx to its factory version while leaving it installed and enabled, so it still wins Home on its priority. Neither cmd role add-role-holder android.app.role.HOME com.gathin.tvhome nor cmd package set-home-activity --user 0 com.gathin.tvhome/.MainActivity moves Home while launcherx is enabled: the role holder is recorded on the new launcher and Home still opens launcherx, and set-home-activity answers Success and changes nothing, so the system Home picker behind it behaves the same. Only disabling the package does, which after enabling it again needs ADB: pm disable-user --user 0 com.google.android.apps.tv.launcherx, then cmd package clear-package-preferred-activities, because the preferred-activity record still names launcherx and Home otherwise falls through to FallbackHome, a blank screen. Plan for it before enabling launcherx on a set where the person cannot re-open ADB themselves.

  • Ambient mode's "Select albums" closes at once, or says "Unable to load content", while the slideshow itself keeps playing with launcherx and setupwraith off. Screen saver > Ambient mode settings (am start -a dreamx.two.panel.SETTINGS -p com.google.android.apps.tv.dreamx) > Google Photos Settings > Select albums starts PhotosSetupActivityV2, which stops with the same Failed to find provider info for com.google.android.apps.tv.launcherx.coreservices.account.AccountModeContentProvider the Google Home panel bullet above names: the album list is launcherx's. Setupwraith is needed as well - with launcherx on and the wizard off the identity check passes, and then launcherx's OnboardingActivity answers "Unable to load content. There was a problem loading your albums." Turn both on (pm enable com.google.android.apps.tv.launcherx, pm enable com.google.android.tungsten.setupwraith), force-stop launcherx so its onboarding does not come up in the picker's place, change the albums, then return Home and let check.py --fix disable both again and put the new launcher back on Home. Home is launcherx's while it is enabled. Seen on a Sony XR-77A80L (BRAVIA_VH22, Google TV 12, STT2.230505.001.S136, #2854).

  • Large pushes hang through the Python client. Use tools/b64push.py for APKs.

  • tools/tv.py reboot never comes back. reboot is a shell command, and adbd stops before it sends the shell's last CLSE, so the client waits for a reply that cannot arrive: two minutes, then an error, on a TCL 65C935 (G05_4K_GB, Google TV 11, AR2101, #995). A chain that reboots and then checks something stalls there. Use tools/tv.py --reboot: it sends the reboot through ADB's own service and returns once the request is accepted. The TV is still restarting at that point, so let it answer again (tools/tv.py --probe) before the next command.

  • logcat fills with Failed to find provider info for com.google.android.tungsten.setupwraith about twice a second once the wizard is disabled. On a Sony XR-77A80L (BRAVIA_VH22, Google TV 12, STT2.230505.001.S136 682241) com.sony.dtv.sonysystemservice asks for that provider every 500 ms for as long as com.google.android.tungsten.setupwraith is disabled: it starts about three minutes after boot and piled up 3,617 lines in the first half hour, some 7,000 an hour, all from that one process (pidof com.sony.dtv.sonysystemservice was its only caller). The wizard being off is the whole cause, and nothing is broken: pm enable com.google.android.tungsten.setupwraith stopped it within the second and disabling it again started it again, nothing on screen changes, and idle CPU for the whole TV still fell from 22.6% to 9% after the debloat and the launcher swap. Turning the wizard back on is not the quiet way out either, since its .RecoveryActivity takes Home from the launcher: leave it off and let the line repeat. Seen on that set (#2853) and on a VH2, an XR-55X90JK (BRAVIA_VH2_SA, Google TV 12, build STT2.230505.001.S136 682241) with setupwraith and launcherx disabled: there the line came every 500 ms from a single process, each time with CountrySettingsModule: SetupProviderGateway getCountryCode() failed to query SetupProvider., and nothing on screen was wrong. Whether VH21 units do the same has not been reported yet.

  • Every dumpsys answers Service dumps disabled due to hung system process., and tools/disable.sh skips every package in the list and says to reboot first (older copies of the script said "the TV's dumpsys shows neither a requested nor a granted permission block" and named Android 5.x instead). Android's watchdog turns service dumps off once it has seen system_server hang, and they stay off until system_server restarts. dumpsys activity and dumpsys connectivity print nothing useful either, so the Home check cannot read the focused app. On a Xiaomi Mi TV 4A (MiTV-MOOQ1, Android TV 11, RTM4.220307.164.8277) that had been up for almost eight days, the load average was around 62 and logcat held repeated watchdog lines from system_server. Reboot before changing anything (tools/tv.py --reboot); dumpsys worked again straight after. Do not get past the skips with TVINPUT_ALLOW=1: the check could not run, which is not the same as passing.

  • A logcat without the line you expected is not proof that nothing happened. On a TCL 55C728 (G05_4K_GB, Google TV 11) the main buffer is 1 MiB (persist.logd.size.main), and the TV's own HDMI, ARC and media logging fills it in well under a minute; around power-on it held about 30 seconds. A wake or boot event read a minute later is already gone. Enlarge the buffer for the session with tools/tv.py 'logcat -G 16M' (logcat -g shows the size; a reboot brings back the firmware's), or keep logcat running from before the event. For sound, dumpsys audio keeps a history of player events that outlasts the log.

  • Every way of disabling a package is refused, not just one. On a Fire TV Box 2015 (sloane, Fire OS 5 / Android 5.1) pm disable-user --user 0 returns SecurityException, pm uninstall --user 0 returns DELETE_FAILED_INTERNAL_ERROR, and pm hide reports success while pm list packages -d stays empty (#577). Read pm list packages -d after each attempt instead of the exit code - pm hide is the one that lies. When all three refuse, this firmware does not take a debloat list at all: there is no shell route around it, sponsored rows stay on the home screen, and the only useful work left is the TV's own privacy toggles plus DNS blocking (Pi-hole with a smart-TV ad list; the report used Perflyst's AmazonFireTV list). Fire OS 6 is the opposite case: pm disable-user works and about ten Amazon packages are protected, which examples/amazon-fire-tv-stick-4k-2018/debloat.txt lists, with the same DNS fallback for their endpoints.

  • pm disable <package>/<class> is refused with Shell cannot change component state, the SecurityException you get when you try to keep a package and switch off only its Home activity or boot receiver. The notes here have it for setupwraith's .RecoveryActivity (TCL G08, Chromecast with Google TV HD, Google TV Streamer), launcherx's .home.HomeActivity (XGIMI MoGo 4), boot receivers on a Fire TV Stick Lite (Fire OS 7) and a Motorola MT4310 (Android TV 11), and the ERGO DVB92YC's Live TV boot receiver below. It is not that firmware: from Android 7.0, AOSP's PackageManagerService lets the shell change only a whole package, and only between default, enabled and disabled-user (from 8.0 a test-only APK is the exception). The same check refuses pm disable and pm disable-until-used on a whole package, which ask for other states, and any change, pm enable included, to a package that is already disabled (enabled=2 in dumpsys package, as tvrecommendations is on a Chromecast with Google TV 4K). There is no shell route around it, so don't retry it with other flags: disable the whole package with pm disable-user --user 0, or keep it.

  • pm disable-user answers Error: java.lang.IllegalArgumentException: Unknown package: <package>, by hand or for one line of a list (#1880, a Konka B50U). Nothing of that name is installed for the user the command names, so that one line changed nothing - and the lines around it still run: tools/disable.sh pipes the list into tools/tv.py --batch, which runs each line as its own shell call and carries on to the next whatever this one answered, so a list with three unknown names in it still finishes, and the client's exit status says nothing about them. What the line is worth is the name in it. Two causes. The list was written on another set: every list here names the model and firmware build in its header, preloads differ between regional builds of the same model, and examples/tcl-c845x2-google-tv/debloat.txt calls its own two Unknown package errors harmless for exactly that reason. Or the name is not a package at all: a store title, a home-screen tile, or a name guessed from the app's look, as com.vendor.recommendations was. pm list packages is the only list of names the TV answers to, and a fragment of the app's name usually finds it:

    tools/tv.py 'pm list packages | grep -i recommend'
    tools/tv.py 'pm list packages -u --user 0 <package>'    # -u: one removed earlier still shows
    

    Nothing from the first command means the app is not on this TV, so comment its line out of the list instead of retrying it - and check the model and Android version tools/tv.py --probe prints against the header of the list you are following. A name the TV does list is the case the bullet below covers: tools/recovery.py records a listed package that was already gone before the first run as absent, and restore.sh and check.py say so in their own words.

  • A package removed with pm uninstall --user 0, by hand or by another debloat tool, stays gone after tools/restore.sh. The Toshiba 55Z500QF note (Hisense firmware, Android 11, RD2A.211001.002) has 23 preinstalled packages its owner removed that way. The command takes a package away from user 0 only, and a preinstalled one keeps its APK on the system partition. pm list packages -u --user 0 <package> still names it (-u adds uninstalled packages) and the same command without -u does not; the User 0: line of dumpsys package <package> says installed=false. tools/recovery.py records a listed package that is already gone when tools/disable.sh first runs as absent, so restore.sh prints leaving <package> alone (absent when the record was taken), and tools/check.py lists it under "not installed any more". Bring it back with tools/tv.py 'cmd package install-existing --user 0 <package>' (AOSP's cmd package has it from Android 8.0; the Android 6 set in #1185 had no such command; no TV here has run it yet). It comes back without its data unless the uninstall used -k, and disabled if a list that names it has gone through disable.sh since it was removed: that run sent it pm disable-user --user 0, which AOSP applies to a package removed for user 0 too (the Shield list notes disable.sh reporting "disabled-user" for such packages), and install-existing marks it installed without changing that state. pm list packages -d --user 0 <package> names it if it came back disabled. To keep it, comment its line out of your list, then run tools/tv.py 'pm enable --user 0 <package>': with the line still in, check.py reports it as back on, and --fix or the next disable.sh run disables it again. If you don't want it, keep the line: the list disables it, or keeps it disabled, which the TV's own settings can undo. --fix or a disable.sh run that finds it enabled first changes it from absent to enabled in the record, so restore.sh turns it back on too. Until then, or in a record an older version updated, it stays absent, and restore.sh leaves it alone: pm enable --user 0 <package> is its undo. Two cases this does not cover. An app with no copy on the system partition that only user 0 had is deleted with its APK: the command answers Package <package> doesn't exist, and only reinstalling the APK brings it back (the Toshiba's com.hisense.business.ad). And a User 0: line with installed=true hidden=true was hidden, not uninstalled. install-existing changes nothing there even though it prints Package <package> installed for user: 0, and the shell cannot unhide it: pm unhide needs MANAGE_USERS, which the shell does not hold from Android 7.0. Unhide it from the device-owner app that hid it, or leave it, as the Chromecast with Google TV 4K list does with cast2class (hidden=true and enabled=2 there).

  • A second Google TV profile keeps the listed packages enabled for itself. The lists, tools/disable.sh, tools/restore.sh and tools/check.py act on user 0, the TV's main profile. A profile added on Google TV is a user of its own (type com.android.tv.profile in dumpsys user), with its own enabled state for every package, so the list never reaches it. On a Chromecast with Google TV 4K (sabrina, Google TV 14, UTTC.250917.004) with one such profile, six packages the list had disabled for user 0 were still enabled for that profile, user 10: com.google.android.videos, com.android.tv.feedbackconsent, com.google.android.adservices.api, com.google.android.ext.adservices.api, com.google.android.federatedcompute and com.google.android.ondevicepersonalization.services. They run only while that profile is in use; it had not been since setup. See which profiles there are, and what each still has on:

    tools/tv.py 'pm list users'                    # UserInfo{10:...} after user 0 is a second profile
    tools/tv.py 'pm list packages -e --user 10'    # what that profile has enabled
    

    To clean that profile as well, run the list's names for its user by hand (both lines below were run on that Chromecast). The recovery record, restore.sh and check.py do not see it, so undo it the same way:

    tools/tv.py 'pm disable-user --user 10 com.google.android.videos'
    tools/tv.py 'pm enable --user 10 com.google.android.videos'          # undo
    
  • The Input key and CEC die after the hidden factory menu is opened, seen on a Haier/CVTE set (Durabrand DURAT32ISDB, dupont, Android TV 11 build RTM6.230109.232, #470). com.cvte.tv.hkcfac is the manufacturer's production app: dumpsys package shows it as a factory component, and it has a launchable activity, so "let me see what this package does" is enough to trigger it. Opening it sets customer_factory_mode=true in /vendor/tvcertificate/fdata.ini and vendor.mtk.factory.disable.input=1. The Input button then does nothing and HDMI-CEC is off, and at the next boot com.at.factory.factorymanager disables Play services, Netflix, com.google.android.katniss and com.google.android.apps.mediashell - pm enable from the shell is refused for those, so casting and the store stay gone until the mode is cleared. Read the package instead of starting it. If it has already been opened, start it, send the factory-exit keycode twice with it in front, reboot, then turn CEC back on:

    tools/tv.py 'am start -n com.cvte.tv.hkcfac/.MainActivity'
    tools/tv.py 'input keyevent 218; input keyevent 218'
    tools/tv.py --reboot             # returns once ADB accepts it; wait for the TV to come back
    tools/tv.py 'settings put global hdmi_control_enabled 1'
    

    Press the real Input button afterwards and check the packages are back: tools/tv.py 'pm list packages -e | grep -E "gms|netflix|mediashell|katniss"'. More on the unit in the device notes.

  • Stremio looks like its server died after the debloat, seen on a Xiaomi MiTV-MZTU0 (river, Google TV 14, list). The app opens, the add-ons load, and nothing ever plays, which reads like a local server fault. It is not: with com.google.android.apps.mediashell disabled, Stremio's own stremio_server_process is still running, still listening on 11470/12470, still holds ESTABLISHED connections to api.strem.io, and logcat -b crash is clean. Stremio's Android TV build plays through mediashell, so the server is healthy and the playback path is gone. pm enable --user 0 com.google.android.apps.mediashell fixed it on that set with no reboot and no reinstall. Check this before blaming your add-ons or the network:

    tools/tv.py 'pm list packages -d | grep mediashell'     # output here = this is the cause
    tools/tv.py 'pm enable --user 0 com.google.android.apps.mediashell'
    

    Keep the package out of your list if you use Stremio on the TV, or cast from a phone.

  • HDMI ports detect the cable and CEC device, but switching to an HDMI input shows "No Signal" on a OnePlus TV (65" C00 Cebu, Android TV 11 build RTM8.240401.135, #321; list). dumpsys hdmi_control still sees the connected device (e.g. a PlayStation 5 on HDMI 3, physical_address: 0x3000, port_id: 3), yet opening the port shows "No Signal, please check the connection of input source." after three seconds. When the OnePlus Live TV / input app (com.heytap.tv.livetv) opens an HDMI port, its IPCManager binds to com.heytap.tv.support/.server.TvSupportService (action: com.heytap.tv.ACTION_SUPPORT) before tuning the MediaTek hardware session (com.mediatek.tvinput/.hdmi.HDMIInputService/HDMI300004). With com.heytap.tv.support disabled (its name reads like a customer-support app, so a hand or assistant run that calls pm disable-user directly can disable it; tools/disable.sh skips it because it asks for android.permission.TV_INPUT_HARDWARE), logcat prints Unable to start service Intent { cmp=com.heytap.tv.support/.server.TvSupportService } U=0: not found and IPCManager: [IPCManager.java.bind#177]bind:: tv start service is null, and dumpsys tv_input shows mainSessionToken: null. A full restore and cold boot are not needed — re-enabling that single package restores the HDMI signal immediately with no reboot:

    tools/tv.py 'pm list packages -d | grep com.heytap.tv.support'
    tools/tv.py 'pm enable --user 0 com.heytap.tv.support'
    
  • Picking HDMI 1 in the Inputs menu opened an unrelated app, seen on an RCA Android TV 9 (Toptech/Zeasn firmware RTK2851P, Realtek RTD2851P, #805). The launcher switches to a passthrough input with an implicit ACTION_VIEW on the input's channel URI (vnd.android.cursor.item/channel), and only sends the theme's input_action when nothing claims that intent at all. "Nothing claims it" is the catch: that firmware has no Live TV app for the URI, and ES File Explorer (com.estrongs.android.pop/.app.transferstation.FileTransferStationActivity) declares a VIEW filter for the same MIME type, so HDMI 1 opened the file manager and input_action was never reached. A set with no handler at least shows "Not available on this TV"; a set with one wrong handler opens that app. Ask which app would take a row before blaming the launcher:

    tools/tv.py 'cmd package query-activities --brief -a android.intent.action.VIEW -t vnd.android.cursor.item/channel'
    

    live_tv_package does not help: it is applied as setPackage on the Live TV row only, and the HDMI and AV rows carry no package. Until the launcher checks that the handler is a TV-input app, hide the rows instead: a theme with source_panel_service set and live_tv_package empty makes the Inputs pill open the TV's own source panel (what the reporter did), or disable the app that answers when it is not a Live TV or input app. tools/disable.sh (disable.ps1) keeps any listed app that query names and skips it with "the TV opens its HDMI/AV inputs and channels with it", so put its line in a list of its own and apply that with HANDLERS_ALLOW=1 ($env:HANDLERS_ALLOW): the rest of your list keeps the guard. Run tools/venv/bin/python -I tools/check.py --fix on that list with the same switch, or it leaves the app on and exits 1. disable.ps1 reads the switch once and clears it from the environment, so one $env:HANDLERS_ALLOW=1 covers only that one run (#1845); check.py --fix still reads it, and in PowerShell it stays set until the window is closed: run Remove-Item Env:HANDLERS_ALLOW after those two, or a later check.py --fix on your main list skips the guard too. To turn the app back on, use tools/tv.py 'pm enable --user 0 <package>'. tools/restore.sh with that list turns it back on as well, and leaves Home on this project's launcher: it turns the stock launcher back on and makes it Home only if the stock launcher was on when the list was applied (#1714). Report another one rather than reading it as a firmware quirk: the wrong-handler case needs a source change, not a setting.

  • adb exec-out screencap -p gives a broken PNG on some MediaTek sets (Xiaomi, and Amazon's own Fire TV smart TVs): the binary prints Init wrapper sys mutex successful on stdout first. Save on the TV and pull the file instead: screencap -p /data/local/tmp/s.png, or cut everything before the \x89PNG header. tools/tv.py --screenshot shots/home.png does the save, the pull and the cleanup in one command, and says so when the capture is 0 bytes.

  • Gradle says "Your project path contains non-ASCII characters" on Windows before building the launcher. Android's build tools reject a checkout under a localized folder such as 桌面 (Desktop). This was seen while installing on a Sony KM-55X85J (Google TV 12, Taiwan firmware); the TV is not changed when the build stops here (#1777). Clone the repo into an ASCII-only path such as C:\cleanyourtv, or map a drive letter to the checkout's parent directory and run the build through it:

    subst X: 'C:\Users\<user>\OneDrive\桌面\sideproject'
    Set-Location X:\cleanyourtv
    powershell -ExecutionPolicy Bypass -File launcher\build-and-install.ps1 -TvHost <tv ip>
    Set-Location C:\
    subst X: /D  # after the build is finished
    
  • Play Protect blocked the launcher ("unsafe app blocked, built for an older version of Android") and pm install hung, then failed with INSTALL_FAILED_VERIFICATION_FAILURE, while it targeted SDK 29. It targets 34 now; if you built an old checkout, rebuild.

  • pm install fails with INSTALL_FAILED_OLDER_SDK on a set older than the launcher: the APK's minSdk is a compile-time floor, so a TV on Android 7.0/7.1 (SDK 24/25) cannot take the published build at all (#576). Read both numbers before changing anything: tools/tv.py 'getprop ro.build.version.sdk' and grep minSdk launcher/app/build.gradle (26, i.e. Android 8.0, at the time of writing). launcher/build-and-install.sh now asks the TV first and stops with both numbers, so nothing is built or pushed for a set that will refuse it; build-and-install.ps1 only reports it after pm install, which fails safely with the stock launcher untouched. Lowering minSdk is a source change, not a setting, and it means maintaining your own copy: the launcher uses AndroidX and framework calls newer than API 25, so expect compile errors or a launcher that starts and then crashes. An APK produced by rebuilding the project elsewhere (an assistant, an online builder, a "rebuilt for API 25" zip from a forum) is not something this repository can check, and the install scripts only push the APK they compiled themselves: a build signed with another key does not install over this one (INSTALL_FAILED_UPDATE_INCOMPATIBLE), so uninstall the old one first if you go that way, and keep the stock launcher enabled until the new one really starts. On a set below minSdk the debloat and privacy half still applies, from the TV's own home screen: it is the launcher that cannot be installed, not the lists. If the Failure [...] line names INSTALL_FAILED_VERIFICATION_FAILURE instead, that is the bullet above - the firmware's installer verifier, not the API level.

  • App info opens half-width with a dark rectangle from a tile's long-press menu on a TCL G07 (55P61B, Android 11, #1696). The TV's AppManagementActivity renders the same way when opened by the standard android.settings.APPLICATION_DETAILS_SETTINGS intent from adb, with the launcher out of the path. To view an app's details on this firmware, open TV Settings > Apps and select the app there: Settings uses its AppsActivity with an embedded detail pane, which the owner checked renders at full width. Changing the launcher's intent flags would not address this firmware's standalone App info screen.

  • Screenshots come out empty while Live TV, an HDMI input or Netflix is on screen, seen on TCL and a Sony KDL-49W800G. Press Home before screencap. An empty capture means nothing about the ADB connection or playback; check those separately.

  • Screenshots come out identical instead of empty, seen on an Amazon Fire TV Stick 4K 2nd gen (AFTKRT, karat, Fire OS 8) right after a reboot: screencap -p kept returning the same non-empty PNG byte-for-byte (same MD5) across completely different resumed activities, minutes apart. It looked like a real (black) capture and cost real debugging time before md5-comparing two pulls gave it away. dumpsys SurfaceFlinger --display-id showed no identification data for Display 0 at the time. Don't trust a screencap pull on its own here: diff it against another pull, or check dumpsys activity activities | grep mResumedActivity and real key events (adb shell getevent -lt) instead.

  • The whole UI is huge and clipped, and it is not the launcher, seen on a Sony K-85XR70 (BRAVIA 4K AE2, Android 14, China firmware, #590). Its UI runs at 1920×1080 on a 4K panel by design (#29), and two confirmed boots came up with an override density of 640 where 320 worked: everything doubles in size, tiles clip, and it looks like a grid bug. Read the overrides before touching the layout:

    tools/tv.py 'wm size'          # Physical size vs Override size
    tools/tv.py 'wm density'       # Physical density vs Override density
    tools/tv.py 'settings --user 0 get secure display_density_forced'
    

    An Override size left behind by an old resolution experiment is the first thing to explain, and an empty saved density is a normal Android default rather than damage. wm density reset fixes the session; on this firmware it came back as 640 at the next boot, and wm density 320 alone did not stick because it left the saved setting empty. Put settings --user 0 put secure display_density_forced 320 after it, reboot, and read all three lines back before believing it. Don't copy the number to another TV: record the three values first, and undo with wm density reset plus settings delete secure display_density_forced. Device note: Sony K-85XR70.

  • Disable the stock launcher only after the new one is installed, or the Home button lands on a factory test app. Launch the replacement by hand first. set-home-activity can report Success while HOME still resolves to the stock launcher (priority 2) until that one is disabled. And check which packages actually handle HOME before disabling anything by name: on the Sony above, com.google.android.leanbacklauncher has a boot receiver but no HOME activity. tools/disable.sh now does that check itself: when the list holds the app that owns Home and nothing else on the TV answers HOME, it skips that entry and says so (FORCE_HOME=1 overrides), so applying a list before the handover no longer leaves the TV without a launcher. Apply the list again after the install script has made the new launcher Home to disable the old one. A factory test app does not count as something else: on a Xiaomi Mi TV 4A com.duokan.factorytest/.FactoryLauncher answers HOME, so the guard leaves it out, along with any package named *factorytest*, *factory_test* or *factorymode*.

  • The TV no longer boots after a list that disabled Android's core packages (original Mi TV Stick, #884). An agent's list took com.google.android.permissioncontroller, com.google.android.packageinstaller, com.google.android.ext.services, com.google.android.ext.shared, com.google.android.modulemetadata and the com.google.android.overlay.modules.* overlays, plus the stock launcher and setupwraith, and the stick never came back. Those are part of Android itself, not bloat. tools/disable.sh, disable.ps1 and check.py --fix now refuse any list that names them (or com.android.providers.* other than calendar, contacts, user dictionary and blocked numbers, com.android.systemui, com.android.shell, com.android.phone, com.android.settings, com.android.tv.settings or android), with no switch to override it, and since #1578 the same refusal covers the TV middleware: com.cltv.mal, com.mediatek.tis, com.mediatek.tv.service and com.mediatek.tv.agent (the bullet below). The three tools say "core Android or TV middleware packages" in that message, so a run that refuses one of the four reads the same way as this one. If a set is already stuck and still answers ADB, pm enable each of those packages and reboot; if ADB is gone too, a factory reset from the recovery menu is usually the only way back.

  • The TV restarts in a loop after a debloat, seen once, on a Haier H50H5F (Google TV 14, MediaTek, #1578). What the agent disabled is not known. The reply on the issue names eight packages from the disabled-package list attached to the report: com.cltv.mal (TV middleware), com.cltv.hybrid (Live TV), the ISDB and ATSC tuner inputs and four Haier apps. But the six files attached match the backup PROMPT.md has the agent save before changing anything, and on the Haier-built AQUA MatrixTV EE 50 five of those eight are disabled from the factory. So the list may be the TV's state before the debloat: then it says nothing about the cause, and turning those eight back on would switch on what the factory left off instead of undoing the debloat. If the set stays up long enough between restarts, pm enable with the stock adb each package from the list that was applied (or run the agent's restore script), and reboot. Wireless debugging usually goes off at every restart and gets a new port each time it is switched on, so read the port from its screen or adb mdns services. This set never stayed up long enough to switch it back on, and then restarted into the recovery menu, where a factory reset turns every disabled package back on (nothing was uninstalled). One of those eight, com.cltv.mal, is on the list tools/disable.sh, disable.ps1 and check.py --fix refuse (the bullet above), so a debloat cannot take it out again; the other seven are not guarded.

  • The TV stays on the boot logo after a list, and disabling the only Home app is enough to do it, seen on a KitKat-era set (COOP COTV-0001: Realtek rtd298x, Android 4.4.2, SDK 19). 17 packages were disabled over Ethernet ADB, each answered new state: disabled-user and pm list packages -d read back all 17; the factory launcher com.haiersoft.launcherproject2995hk was among them with no replacement, because the launcher here needs Android 8.0. After the power cycle the set sat on the green robot and INICIALIZANDO for 20+ minutes, across a hard power cut and another off/on from the remote. The reporter then isolated it: disabling that one package reproduces the hang and re-enabling it boots the set, so the batch size and the power cut were not the cause. Two things to carry away. First, the screen being stuck does not mean the set is gone: this one still answered ADB on Ethernet while it said INICIALIZANDO, and pm enable on every package from the pre-change backup plus a reboot brought it back (the recovery steps are in the device notes). Second, a vendor package that looks unrelated can be what the launcher runs on - here disabling com.tsb.tv stopped the launcher with "Se ha detenido la aplicación lancherProject" instead of breaking the boot - which is why a list goes on in batches with a reboot and a look at the screen between them, on a set that cannot take the launcher especially: leave the stock launcher alone, and keep ADB on Ethernet (KitKat has no network-debugging toggle, and Wi-Fi closed 5555 on this one). tools/disable.sh skips Home's owner when it can name it; since #795 it asks dumpsys activity when cmd package is missing (Android 6 and older, where that command does not exist) and warns when neither answers. That warning means the guard did not run, not that the list is safe. Without ADB and without a vendor recovery image, a set stuck this way is still out of reach.

  • China-market Xiaomi rejects a default Home despite set-home-activity reporting success, verified on MiTV-AETP0 (dofus), Android 9, firmware 22.9.2.851: the framework filters third-party HOME candidates by a package-name allowlist. Disabling the stock launcher then selects FallbackHome. An opt-in application-ID build worked with the stock launcher still enabled; see the device notes and rollback steps. This is different from a missing default-home picker or a remote that emits ALL_APPS.

  • China-market Xiaomi refuses the new launcher as Home outright, seen on a Laser Cinema 2 4K (nemo, Android 9, firmware 2.1.3.4134): cmd package set-home-activity answers cannot be home on user 0, while the APK does declare MAIN/HOME/DEFAULT and is installed and enabled. This firmware's PackageManagerService.applyPostResolutionFilter removes every HOME candidate that is not FLAG_SYSTEM when ro.formovie.launcher_type is not AndroidTV (it reads Patchwall here, and a shell setprop is refused), so this is a flag test rather than the package-name allowlist above and the alternate application ID it accepted does not help. query-activities -c android.intent.category.HOME lists only PatchWall and FallbackHome. Keep the stock launcher enabled - disabling it leaves only FallbackHome - and see the device notes for the queries and the firmware code.

  • ColorOS TV refuses a HOME install, or boots only a launcher that declares its own category, verified on an OPPO K9 55 (OPPOCNM632), Android 9. The default com.gathin.tvhome build is not accepted as Home. The working build is -PlauncherApplicationId=com.oneplus.tv.launcher2, and MainActivity's HOME filter also declares com.heytap.tv.launcher.category.MODERN_HOME (without that category, ColorOS boot waits on the brand animation). Leave com.heytap.tv.launcher installed and enabled: it is the stock home, and building over that package name replaces it. launcher/build-and-install.sh installs com.gathin.tvhome and would disable a stock launcher it was told to, so do not name com.heytap.tv.launcher as STOCK_LAUNCHER. The other allowlisted ids are com.heytap.tv.launcher (stock, do not overwrite) and com.oppo.tv.launcher2. The list is at examples/oppo-k9-55-oppocnm632/ and the full note is docs/devices/oppo-k9-55-oppocnm632.md.

  • The new launcher still doesn't own Home after the stock one is disabled, seen on Google TV (Xiaomi MiTV-MOOR2). Another system app answers HOME: com.google.android.tungsten.setupwraith, with .MainActivity at intent-filter priority 4 and .RecoveryActivity at priority 1. You cannot outrank it, and the obvious workarounds don't help: Android ignores android:priority on HOME filters for apps that aren't on the system image, cmd package set-home-activity prints Success without changing anything (not even after a reboot), and pm disable-user on one of setupwraith's components is refused with a SecurityException. Disabling the whole com.google.android.tungsten.setupwraith package is what frees the slot. It is the first-run setup wizard, so everyday use is unaffected, but re-enable it before a factory reset - list it in your debloat.txt and tools/restore.sh does that for you. Measured again on a Google TV Streamer (kirkwood, UTTK.260317.003): a launcher built with android:priority="3" on its HOME filter (aapt2 dump xmltree finds it in the installed APK) still has no mPriority line in dumpsys package, and cmd role get-role-holders android.app.role.HOME names the new launcher while the HOME key lands on setupwraith/.RecoveryActivity anyway. cmd package resolve-activity and query-activities answer with the resolver table's winner, which is not necessarily the activity that takes focus: dumpsys activity activities | grep mFocusedApp is the ground truth, and it is what to check before saying the takeover worked.

  • Home works when tapped but does nothing when pressed firmly, after the stock launcher is disabled, seen on a Philips 65OLED804/12 (TPM191E, Android TV 12, STT2.230526.001) with a slow third-party remote whose presses last 430 to 550 ms. Android turns a Home press held past 400 ms into its long-press action, and dumpsys window policy shows it is mLongPressOnHomeBehavior=LONG_PRESS_HOME_ALL_APPS. PhoneWindowManager.launchAllAppsAction sends ACTION_ALL_APPS to the system-image Home only (MATCH_SYSTEM_ONLY), which with the stock launcher disabled is com.android.tv.settings/.system.FallbackHome, and nothing answers it there: logcat shows START u0 {act=android.intent.action.ALL_APPS pkg=com.android.tv.settings} 400 ms after each key down and the TV stays where it was. Before the debloat the stock launcher answered with its app grid, so it looked like Home. An injected input keyevent --longpress KEYCODE_HOME reproduces it and a plain input keyevent KEYCODE_HOME does not. settings put secure long_press_timeout 800 does not move the limit, not even after a reboot. home_long_press_home in config.xml or a theme, with HOME_KEY_SERVICE=1 at install, makes HomeKeyService take every Home press and open Home on release. That needs the helper to filter keys, so it costs the held-key repeat described under the key-repeat section. Confirmed on this set: with HomeKeyService bound, Volume Down held for 3 s gave one step in dumpsys audio; unbound, a 1.07 s hold gave 14 repeats 50 ms apart. Single presses step once either way, also through HDMI ARC to a home cinema system. Choose between a firm Home press and a held volume ramp; this set cannot have both. A 50PUS7354 on the same board and firmware does the same with its original Philips remote, so a slow remote is not needed: firm presses sent ALL_APPS to FallbackHome there too, and home_long_press_home fixed it.

  • The TV drops off Wi-Fi for good the moment setupwraith is disabled, seen on Google TV 14 (Sony BRAVIA 9 II, Philips R8G, Skyworth SWTV-24NA-4K NF, Xiaomi TV Box S 3rd gen MiTV-AFMU0) and on a TCL G07 on Google TV 11 (build AR17; on AR20 the network belonged to android.uid.system). A Wi-Fi network saved during the first-run wizard belongs to setupwraith, and Android deletes an app's saved networks when the app is disabled. The TV is fine but offline, so ADB and the phone remote stop working, and a restart doesn't bring the network back. Check before disabling it: tools/tv.py "dumpsys wifi" | grep -o 'cname=[^ ]*'. If it names setupwraith, forget the network and join it again in Settings > Network & Internet (it then belongs to android.uid.system), or use Ethernet. tools/disable.sh skips setupwraith while it owns the network (FORCE_SETUPWRAITH=1 overrides). Already offline? Rejoin from the TV with its own remote or a USB keyboard, then turn Wireless debugging back on if it was in use. Reconnecting is not enough; forget the network first (Xiaomi MiTV-MOSR4, where this also switched USB debugging off).

  • A reboot on setupwraith's recovery screen factory-reset the TV (Xiaomi MiTV-MOSR4). Disable setupwraith before launcherx. If mFocusedApp shows .RecoveryActivity, re-enable launcherx first and do not reboot. Notes.

  • Home worked for days, then went black after a power cut (Acer R4G and Panasonic 4KTV-3MUP, MOKA R4_GTV, Google TV 14): the firmware re-enables setupwraith at every cold boot and its .RecoveryActivity takes Home. logcat | grep RecoveryActivity shows it starting several times a second. Re-enable launcherx as above. Notes.

  • Black screen at every boot after the launcher change, LED on, remote dead (Vision 2024VISION43STV, CVTE, Android 13: its list has the details). With file-based encryption (getprop ro.crypto.type says file) Android starts Home before the first unlock, and only a direct-boot-aware activity can start then. Most firmwares have a FallbackHome that stands in until the unlock; some, like this CVTE board, do not, and then boot never completes. This repo's launcher is only partly direct-boot-aware: HomeKeyService declares the flag, so Android binds it while the user is still locked, and GuardRevival declares it too (neither that listener nor the standby opt-in is on in an ordinary install). MainActivity, the activity that answers HOME, does not declare it. The second command below therefore prints PARTIALLY_DIRECT_BOOT_AWARE for this launcher and cannot say whether Home exists before the unlock; on a set with no FallbackHome, the first command is the one that decides. Check both before disabling the stock launcher:

    tools/tv.py 'cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.HOME'
    tools/tv.py 'dumpsys package <new.launcher.package> | grep -o "[A-Z_]*DIRECT_BOOT_AWARE"'
    

    No FallbackHome in the first and nothing from the second: keep the stock launcher enabled and set Home with cmd package set-home-activity only. That line is not proof the handover happened: on a set whose stock launcher declares a HOME priority it can print Success and change nothing, with the Home role already naming the new launcher while Home still opens the stock one, so read the resolver instead (tools/tv.py 'cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.HOME'). When it keeps naming the stock launcher, the set is debloat-only until a boot with the stock launcher disabled has been tried on that exact board - which does not mean the launcher is unusable there: it still opens from the app list and runs, with Home left where it is. The two checks have been run on the Thomson 40FG2S14W (Expressluck nagata, Android TV 14 UKNV.260317.001) and both fallbacks are there, but that boot has not been tried (notes, #1350).

  • Stuck on the boot animation after the launcher change, ADB gone (JVC LT-43N7115, CVTE TP.SK706S.PC822, Android 11, #741): the stock launcher com.cvte.tv.launcher.space was disabled and a remote-key helper enabled before one reboot. Nothing was uninstalled, so try in this order: wait ten minutes once; boot to safe mode (hold Power on the remote during the animation on many sets), which turns off added apps, then re-enable the stock launcher under Settings > Apps > show system apps; keep retrying adb connect <ip>:5555 for a couple of minutes right after power-on and run pm enable com.cvte.tv.launcher.space and settings put secure enabled_accessibility_services "" if it answers (a phone hotspot with the old network's name and password stands in for a missing router); wipe the cache partition from the recovery menu. A factory reset also works but erases everything, so it comes last. The prompt now keeps the stock launcher enabled on CVTE boards without a tested list.

  • Accounts & profiles is empty after the stock launcher is disabled (TCL 75C845X2, G08_4K_GB, Google TV 14, #304). The accounts are still there (dumpsys account lists them); the page lives in com.google.android.apps.tv.launcherx, and while launcherx is enabled it takes Home back (set-home-activity prints Success and changes nothing, and its home activity can't be disabled from the shell). To switch profile: pm enable --user 0 com.google.android.apps.tv.launcherx, switch in Settings, then disable it again and set Home last.

  • Voice search for a song or video says "Google Assistant is having trouble processing your request" once launcherx is disabled, while app commands ("open YouTube") still work. Seen on an onn 4K Streaming Box (YOC, Google TV 14, URO4.260304.011.B1.15051976). Google TV shows those results in its own home screen: the Assistant sends VIEW https://tv.google.com/... to launcherx (.gateway.LauncherDeepLink, then .home.HomeActivity lists the results, and picking one opens YouTube by package). With launcherx disabled there is nothing to open, and logcat shows Failed to execute provider.OPEN with status: UNKNOWN_ERROR within 100 ms. Two things make it look like something else: right after launcherx has run, the Assistant keeps serving results for a while even when it is disabled again, and the failure returns only after a reboot; and nothing in the debloat list matters - with every other package re-enabled it fails the same way. Apps only mode (Settings > Accounts & sign-in) is no way out: it switches the Assistant off. What works: leave launcherx enabled (it is then Home, see the entry above) and open this launcher from its app row, where voice search works normally because the results screen comes up over it; or search inside YouTube with its on-screen mic. The Projectivy community found the same (voice results "need the launcher X search results page").

  • Every mic press answers "Sorry, not available in this profile yet" once launcherx is disabled, seen on a TCL 75Q7CXB (G08, Google TV 14, UTT2.250416.001 AU02) with this launcher as Home. The Assistant opens and hears the press, then gives up before listening: logcat has katniss_interactor_KatnissVoiceInteractionUtilsImpl: Assistant is not enabled in LauncherX kids mode or privacy mode. Will punt. It reads that mode from launcherx, so with launcherx disabled it cannot tell and refuses, whatever profile is signed in. Nothing in the debloat list is involved; katniss and com.google.android.tv.remote.service were both enabled. What works is the same as in the entry above: launcherx enabled, with this launcher opened from its row or reached through stock_home_redirect (see examples/tcl-q7c-google-tv, "Working alternative").

  • Pressing the mic shows no listening indicator at all, seen on the same 75Q7CXB before the entry above: each press logs AtvRemote.BleRemoteDevice: HTT session initiated, so the remote does send it, then AtvRemote.AppOpsDisclosureService: com.google.android.katniss doesn't have access to the on-remote mic and a SensorPrivacyService line saying katniss started using sensor 1 (the microphone). Switching on Microphone access in the TV's Settings (Privacy > Microphone) brought the button back, and it stayed after a reboot. Don't judge it from dumpsys sensor_privacy: that set read state_type=1 for the microphone both before and after, while the button failed and while it worked.

  • The remote's mic button does nothing, or opens the Assistant and it never hears you, seen on a Xiaomi TV Box S 3rd gen (MiTV-AFMU0, twilight, Google TV 14, V816.0.7.0.UZFAABX). Two packages have to stay enabled, and re-enabling only one of them is what makes this hard to diagnose: com.google.android.katniss is the Assistant itself, and com.google.android.tv.remote.service is the Google TV bridge for the physical remote, which carries the remote's Bluetooth microphone. With both disabled the button does nothing. With only katniss back the Assistant opens, takes AudioRecord (inputSource 6, VOICE_RECOGNITION) and records silence, because the audio never leaves the remote. pm enable on both restores the button without a reboot (run pm enable once per package). To tell the two failures apart: dumpsys package com.google.android.katniss | grep enabled and the same for tv.remote.service (enabled=3 is disabled), then logcat -d | grep AudioRecord right after pressing the button: a session that opens and closes with no recognition is the missing bridge, not a broken Assistant. The AFMU0 list keeps both now; the lists that still disable com.google.android.tv.remote.service include liberton-ltv-24h01at, panavox-google-tv, rca-ai-pont, sharp-aquos-tve21f, sony-bravia-4k-gb-us, sony-xbr-65x900f, sony-xbr-x900e, tcl-32s5k, tcl-40s5400a, tcl-55s470g, tcl-65ep640, tcl-beyondtv2-android-11, tcl-g09-google-tv, xgimi-horizon-s-pro, xgimi-mogo-3-pro, xgimi-mogo-4, xiaomi-mi-box-3 and xiaomi-mi-box-s, and grep -l '^com.google.android.tv.remote.service' examples/*/debloat.txt prints the current set. Those TVs haven't been tested for this, but any of them whose remote has a microphone may show the same symptom. The G10 list has since been tested from the other side: on a 43T6C (G10, Realtek, build STT2.230203.001) a mic press with com.google.android.tv.remote.service disabled logs Unable to start service Intent { act=...AtvSearchAssistantService ... } U=0: not found — that service lives in the package — and re-enabling only it, with every other line applied, brought the button back, so tcl-g10-google-tv keeps it now.

  • The remote's voice button stops working after the package that owns Home is installed or updated, and comes back after one reboot (#1869, a TCL BeyondTV2, board rtd285o, Android TV 11, PPR1.180610.011/AR05, the same model and build as examples/tcl-beyondtv2-android-11/). The button is the firmware's on this build, not the Assistant's: KEYCODE_SEARCH (84) sends the system to the activity that owns Home, which clicks its own voice button, so only the ACTION_ASSIST fired there records - a background one is refused with Recording audio is not allowed for this invocation on R.. Installing or updating that package, a rebuild over a different signing key included, leaves that routing stale: every press is denied until the TV reboots, and then 3 of 3 presses recorded with a 16 kHz mono stream and no denials. Reboot once before judging the button, and take installing the launcher again as no fix - changing which launcher owns Home is not what breaks it, cmd package set-home-activity in both directions recorded normally. If the button was dead before that too, check com.google.android.katniss first: the lists that turn voice off, tcl-beyondtv2-android-11 among them, disable it on purpose. input keyevent 84 goes through this firmware path rather than the launcher's (see "Accessibility helper enabled but the remote button does nothing" below), so the physical button is the only test.

  • Casting from a phone fails with "Untrusted device" after setupwraith is disabled, seen on Chromecast with Google TV HD (boreal) on Google TV 14 build UTTC.250917.004, the build the examples/google-chromecast-gtv-4k/ list was made on. YouTube on the phone says "Untrusted device. ... couldn't be verified", and logcat repeats cast_ClientAuthCredsProvisioning: Loading factory credentials for 1P device, Failed to find provider info for ...CastInfoProvider and cast_shell: No private key, hence empty signature. The Cast receiver (com.google.android.apps.mediashell) loads its factory device credentials from a content provider inside setupwraith (...tv.setupwraith.libs.castinfo.CastInfoProvider), so disabling the wizard takes the credentials with it. Re-enable it and within a minute the log shows SetupWraith|CastInfoProvider: querying cast infos and Created singleton instance of ClientAuthSigner, and casting works. You cannot keep the provider and give up only Home: pm disable-user on setupwraith/.RecoveryActivity is refused with SecurityException: Shell cannot change component state. So either debloat without setupwraith and launcherx (for the 4K: debloat-keep-cast.txt), or re-enable setupwraith after each boot, wait for ClientAuthSigner, then disable it again: the receiver keeps the credentials in memory, so casting survives until the next reboot - and only in that process. am force-stop com.google.android.apps.mediashell followed by the receiver coming back prints Retry loading factory credentials and No private key, hence empty signature again, so anything that restarts it (a crash, a force-stop, an update) needs the same sequence. Re-enabling the wizard is not always enough after a reboot: on a Chromecast with Google TV 4K (sabrina, UTTC.250917.004) no ClientAuthSigner line came within 3 minutes and phones did not even list the device. Restart the receiver while setupwraith is enabled instead: pm enable com.google.android.tungsten.setupwraith, logcat -c, then am crash com.google.android.apps.mediashell. The system starts the receiver again with a new pid, and Created singleton instance of ClientAuthSigner follows within seconds. Before trusting that line, check the pid (pidof com.google.android.apps.mediashell) is still the same 30 s later, then pm disable-user --user 0 com.google.android.tungsten.setupwraith; with no No private key line after that, casting works (#2615). Do not restart it with am force-stop plus am start-foreground-service -n com.google.android.apps.mediashell/.CastReceiverService: started that way the receiver never calls startForeground(), Android kills it after 30 s (ForegroundServiceDidNotStartInTimeException), and the copy that comes back once setupwraith is off has no credentials, although the killed one had logged the signer line. am kill leaves it running with the same pid. Reported on Google's own Chromecast hardware (boreal) and, with the same log lines, on a Google TV Streamer (kirkwood, UTTK.260317.003), where the CastInfoProvider also lives inside the setupwraith APK; the Xiaomi Mi Box S 2nd gen list says casting from a Pixel still works there with setupwraith off. A TCL 65Q7CX1 (G08, Google TV 14, UTT2.250416.001 AU02) casts with it off as well: its receiver signs with jni_client_auth_creds_mediadrm (examples/tcl-q7c-google-tv, eighth unit). On a Google TV Streamer the missing provider also takes the Google Home app's control of the set with it, and that goes at every reboot rather than only when someone casts: the receiver stays up and the set still advertises _googlecast._tcp under its own name, but the Home app lists it as unresponsive, and the receiver logs the same Failed to find provider info for ...CastInfoProvider on the boot that broke it (#2991). There, on a set just rebooted with the wizard disabled, pm enable com.google.android.tungsten.setupwraith and am force-stop com.google.android.apps.mediashell followed by about 45 s for the restarted receiver to log no provider error gave control back, and pm disable-user --user 0 com.google.android.tungsten.setupwraith afterwards kept it for that boot: the credentials live in the receiver's process, so the break is the reboot that follows. Plain pm disable is refused for this package; pm disable-user --user 0 is the form that works, and the one the lists use.

  • Casting disappears although the Cast receiver is still running, seen on a TCL 75QM751G (QM7, Google TV 14) after its list, and the same "alive is not discoverable" conclusion on a TCL BeyondTV2 (Android TV 9, R851T02 V660) in the same thread. Phones and a PC stop seeing the TV, com.google.android.apps.mediashell is up and listening on 8008/8009, and there is no No private key or CastInfoProvider line — so this is not the setupwraith case above, and re-enabling setupwraith does not help. Check discovery instead of the process: tools/tv.py "logcat -d -s cast_shell | grep -E 'Done announcing|responses sent'" and tools/tv.py "dumpsys activity service com.google.android.apps.mediashell | grep WargService". A receiver that is advertising logs MdnsAppFilter: Done announcing., responses sent in 60 seconds: N and Gcm.Register.Success=2, with WargService up. If those are missing while the process lives, the receiver is hung: the QM7 report saw its connections sitting in CLOSE_WAIT on 8009 with unread data. tools/tv.py "am force-stop com.google.android.apps.mediashell" restarts it (the TV starts it again on demand), and advertising returned within seconds, without a reboot. Device notes: TCL 75QM751G. On Android 14 the receiver may be frozen rather than hung: with launcherx disabled nothing keeps it above the cached state, and the cached-app freezer stops it a few minutes after each wake. A TCL Smart TV Pro (G08_4K_US, UTT2.250416.001 AU16) showed isFrozen=true and curProcState=19 in tools/tv.py "dumpsys activity processes | grep -A3 mediashell", and ActivityManager: freezing <pid> com.google.android.apps.mediashell in logcat every two or three minutes, with the same slow and missing discovery and, once, the CLOSE_WAIT sockets on 8009. A force-stop only helps until the next freeze. What lasts is an app that is always running binding the receiver: there, the Home app binding com.google.android.apps.mediashell/.CastReceiverService (action com.google.cast.action.BIND, with the package in its <queries>) moved it to vis with (service) in tools/tv.py "dumpsys activity oom | grep -A1 mediashell", and it has not frozen since. This repo's launcher does not do that today. That the freezing causes the lost discovery is inferred from the two showing up together, not proven: if isFrozen=true is not there, the receiver is hung, as above. The G08 US notes.

  • The TV drops off Wi-Fi every minute right after the install, seen on Google TV 14 (Xiaomi MiTV-MZTU0): the same setupwraith .RecoveryActivity took Home and system_server crash-looped on it. The install script now starts the new launcher before disabling the stock one, sets the home preference last (Android drops it whenever the set of HOME apps changes), then checks where Home resolves and re-enables the stock launcher if it is not the new one. Disable setupwraith as above. To confirm it is this bug: logcat -b crash shows FATAL EXCEPTION IN SYSTEM PROCESS: android.display, and the main log shows setupwraith/.RecoveryActivity being started every 200 ms or so. On a Google TV Streamer (kirkwood, UTTK.260317.003) the same loop came with its own signature - IllegalArgumentException: positionChildAt: container=Task=... is not a child of container=Task=1 current parent=null at WindowContainer.positionChildAt / Task.moveToBack / TransitionController.tryStartCollectFromQueue - and repeated roughly every 47 s, each cycle taking setupwraith and SystemUI down with DeadSystemException too. If you're already in the loop: tools/tv.py 'pm enable --user 0 com.google.android.apps.tv.launcherx'.

  • theme.sh fails with "couldn't create file: Permission denied", seen on TCL G07 (Android 11): the shell is not in the group that may write Android/data. theme.sh stages the files in /data/local/tmp and writes them through the launcher's ThemeProvider, which creates them as the launcher; cp is only the fallback for a launcher installed before that provider existed: run launcher/build-and-install.sh once, then push the theme again.

  • A theme push prints "is on the TV" and nothing changes, seen on Android 14: the shell can write Android/data there, so cp succeeded - but the file it created belonged to the shell (ls -ln shows 2000 1078 instead of the launcher's uid) and the launcher logged W TvHome.Config: ignoring .../config.json: open failed: EACCES (Permission denied). Overwriting a file the launcher wrote itself keeps its owner, so this only bit files that did not exist yet: it looked fine on a set that had been themed before and did nothing on a fresh install or right after a reinstall. theme.sh now removes the shell's copy and writes through the ThemeProvider first (the provider cannot truncate a file the shell owns); on an older checkout, pushing the theme again after launcher/build-and-install.sh has written the files once also fixes the ownership. The wallpapers/ folder went the same way, seen on a Vestel BODRUM (Android 14, UKNV.260428.001): theme.sh made it with mkdir, so it belonged to the shell (drwxrws--- shell ext_data_rw) and the provider got EACCES writing the pictures into it, while the config still landed. theme.sh now leaves that folder to the provider. A set that already has one from an older checkout needs it removed once (it is empty), then push the theme again: tools/tv.py 'rmdir /sdcard/Android/data/com.gathin.tvhome/files/wallpapers'.

  • Google TV comes back after a few remote presses, seen on XGIMI (MoGo 3 Pro, Google TV 14). The remote's app buttons (YouTube, Netflix, Prime Video), the programmable button, the profile button and power all send F keys that the system hands to com.xgimi.xrmservice/.XgimiGlobalKeyBroadcast as global keys (tools/tv.py 'dumpsys window' | grep -A40 mKeyMapping lists them). That receiver runs pm enable com.google.android.apps.tv.launcherx on every one of them, so the stock launcher takes Home back. Keys listed in app_keys are taken by the key services before global key dispatch, so XGIMI's receiver never sees them: examples/xgimi-mogo-3-pro lists the app buttons. Power (F11) can't be taken over, because its sleep and power-menu handling needs system permissions. For that one, run with HOME_KEY_SERVICE=1 and set stock_home_redirect to com.google.android.apps.tv.launcherx: if it comes back, its home screen is sent straight back here, and tools/disable.sh with the list disables it again.

  • No on-screen keyboard after switching launchers, seen on Xiaomi: the default input method pointed at a Baidu keyboard that isn't installed. Fix: tools/tv.py 'ime set com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME'

  • Third-party launchers often charge for basics like a custom wallpaper. That's why this repo has its own.

  • Wrong time zone, clock off by 30 or 60 minutes, seen on Xiaomi in Iran: the TV picked a neighbouring zone, and older firmware ships time zone data from before Iran dropped daylight saving in 2022 (on Android 10 and newer, check with tools/tv.py 'cat /apex/com.android.tzdata/etc/tz/tz_version'). Set the zone yourself: tools/tv.py 'settings put global auto_time_zone 0; cmd alarm set-timezone Asia/Tehran'. If the clock then jumps an hour ahead in spring, a fixed offset keeps it right: tools/tv.py 'cmd alarm set-timezone GMT+03:30'. The same applies to any country whose rules changed after the firmware was built.

  • The network pill says "Wi-Fi" instead of the network name after pm clear com.gathin.tvhome or a reinstall from scratch: clearing the launcher's data also drops the location permission Android 10 needs to read the SSID. pm grant com.gathin.tvhome android.permission.ACCESS_FINE_LOCATION (the install script does this).

  • Home is "Select a Home app" after tools/restore.sh followed by re-applying a list: disabling a package that also claims HOME (PatchWall on Xiaomi) makes Android drop the default. The install script now picks the known stock launcher among the candidates by itself; otherwise choose one on the TV or pass STOCK_LAUNCHER=<package>.

  • tools/restore.sh leaves the packages an earlier batch disabled off, after the TV changed its address mid-cleanup. The recovery record is one file per list and address (~/.cleanyourtv/recovery-<address>-<list hash>.json), so a new DHCP lease - a router restart, or the Wi-Fi forget and re-join the setupwraith step asks for itself - makes the next tools/disable.sh run find no record and write a fresh one from a TV that is already partly cleaned: every package the earlier batches disabled is in it as "already disabled, left alone by undo", and undo at the new address leaves them off. tools/disable.sh now says so on stderr, names the record under the old address and the packages the two disagree about. Run tools/restore.sh at the old address first, or move that record to the new address's name once you are sure it is this set and not a second one cleaned with the same list (#2139).

  • Stremio fails every stream with ConnectException: Failed to connect to /127.0.0.1:11470, and only Force stop brings it back until the next reboot. This is not the debloat. It is a Stremio regression in 1.11 (stremio-bugs#2866): startServer() only runs once the lifecycle is at least STARTED, so a process Android woke in the background never starts the local server, and opening the app reuses that process. On a TV the waker is Stremio's own BootReceiver refreshing the home-screen channels, so it happens on every boot. Leaving the app and reopening does not help (same pid); any fresh process does, am kill included. Keeping the boot-woken process from sticking around fixes it for good: tools/tv.py 'cmd appops set com.stremio.one RUN_ANY_IN_BACKGROUND ignore; cmd appops write-settings'. The second command is not optional: app-ops are written to disk lazily, and on a Vestel shandao set the op was back to allow after the next reboot without it, which looks exactly like the workaround not working. An app started in the foreground is unaffected, and the side effect, that the home-screen channels only refresh while Stremio is open, costs nothing once the recommendation rows are disabled anyway.

  • An app you keep runs in the background after every boot, or stays in memory with a foreground service, seen with streaming apps on a Noblex NoblexTV (shinjuku, Android TV 11, RTO9.230812.001) and a Tornado 65UA1400E (Android TV 11, RTM5.220609.106), and with TvSettings on a TCL G09 (Google TV 12). On Android 9 and newer the package can stay installed and still be kept from running in the background, the same fix as for Stremio above and saved with write-settings for the same reason: tools/tv.py 'cmd appops set <package> RUN_ANY_IN_BACKGROUND ignore; cmd appops write-settings'. The app still opens and plays on screen. Once it leaves the screen, Android holds back its jobs and alarms, takes away its foreground services and, if the app targets Android 8 or newer, refuses its background service starts. Its home-screen channels and push features then only update while it is open, and a music app loses the foreground service that keeps it playing once you leave it. The op does not stop its boot receiver, so the app still starts at every boot and then drops to cached, and one that opens its own screen at boot, like the waipu app on the waipu.tv Stick, still does. Android accepts the op without an error but still runs the jobs and alarms of a package whose uid is below 10000 (tools/tv.py 'pm list packages -U <package>'), a persistent one (tools/tv.py 'dumpsys package <package>' | grep PERSISTENT) and one on the Doze allowlist (tools/tv.py 'dumpsys deviceidle whitelist', which only reads the list when nothing follows it), so check those first. Undo: tools/tv.py 'cmd appops set <package> RUN_ANY_IN_BACKGROUND allow; cmd appops write-settings'. When the cost is a foreground service the app has no reason to hold, as on the G09 (tools/tv.py 'dumpsys activity services <package>' shows isForeground=true long after the app was last opened), take away only the foreground: tools/tv.py 'cmd appops set <package> START_FOREGROUND ignore; cmd appops write-settings'. Android only reads this op when the app asks for the foreground, so a service that is already in the foreground stays there until it next starts: reboot with tools/tv.py --reboot, then check tools/tv.py 'dumpsys activity services <package>' again, which should no longer show isForeground=true. From then on the service runs as an ordinary one that Android can reclaim. Use ignore, never deny: with deny, the app's request for the foreground throws a SecurityException, which crashes an app that does not expect it. Leave the remote, keyboard, cast and input services alone. Undo: tools/tv.py 'cmd appops set <package> START_FOREGROUND allow; cmd appops write-settings'. App standby buckets (am set-standby-bucket) do nothing useful on a TV: whatever you set, Android keeps a system app with a uid below 10000 at exempted and one with no phone-style LAUNCHER activity no lower than active, and it treats a TV on mains power as charging (tools/tv.py 'dumpsys alarm' | grep Parole reads App Standby Parole: true on a Chromecast with Google TV 4K, sabrina, UTTC.250917.004), which lifts the bucket limits on alarms and on every job outside restricted, a bucket the app leaves the next time it is opened. tools/tv.py 'am get-standby-bucket <package>' only reads the bucket (5 is exempted, 10 active), and tools/tv.py 'am set-standby-bucket <package> active' puts back one that was set.

  • Every package in a batch is enabled again after the reboot that follows it, seen on a Hisense HiSmartTV A4 (xiaoyushan, Android TV 11, RTT2.221108.001 / 00.00.01.18). It looks like firmware that resets the list at every boot, but it is the timing of the reboot. Android writes package state to disk about ten seconds after a change (AOSP's WRITE_SETTINGS_DELAY). A tools/tv.py reboot sent straight after tools/disable.sh came before that write, so the TV booted with the old state. dumpsys package <name> showed enabled=0 (default) for every package, and pm list packages -d was empty. The same package disabled, given 12 seconds, then rebooted stayed at enabled=3 (disabled-user), and so did every batch applied that way. Wait 15 seconds after the last pm command before rebooting, the same wait the tested lists already describe between batches. It is the package-state counterpart of the lazy app-ops write above.

  • A firmware update quietly undoes the cleanup. The update installs over the cleaned-up system, so every listed package is enabled again, Home goes back to the stock launcher and new preloads appear that no list mentions. tools/venv/bin/python -I tools/check.py <list> reports the first two and exits 1; --fix disables them again and puts the launcher back on Home. New preloads it cannot know about, so after a big update compare tools/tv.py 'pm list packages' with the list once by hand. On a TV whose list disables the updater, this only happens when you install an update on purpose: enable the updater, update, check.py --fix, disable it again.

  • The TV opens Live TV by itself at every power-on, often on a "please scan channels" screen, with the new launcher already Home: seen on an ERGO DVB92YC (Changhong AI PONT, redwood, MT5867, Android TV 14, UKNV.260514.001). logcat -b events -d | grep wm_set_resumed_activity after a reboot shows com.mediatek.wwtv.tvcenter/.nav.TurnkeyUiMainActivity resumed about 12 s in, a Homwee "hotel" ProtocolActivity on top, then Live TV again at about 25 s. It is started by com.mediatek.wwtv.tvcenter/.util.BootBroadcastReceiver (BOOT_COMPLETED, priority 1000); on Android 14 pm disable of that one component is refused for the shell, and neither the Live TV menu nor Android's Settings has a power-on-input option. Disabling the whole Live TV app would cost the HDMI inputs. Set power_on_home (config.xml or a theme) with HOME_KEY_SERVICE=1: until the first key press after boot or screen-on, HomeKeyService sends that Live TV window back to Home. Ask the person first; someone who watches broadcast TV may want the opposite, boot_to_live_tv.

  • A third-party screensaver never shows, seen on a TCL 65C745 (G05_4K_GB, Google TV 11, AR2101): the TV starts dreaming after the timeout and wakes up in the same millisecond, so nothing appears. logcat shows DreamController: Unable to bind dream service right after [TclAppBoot] ... calleeComponent = 'com.neilturner.aerialviews/.ui.screensaver.DreamActivity', bootType = 'bind_service', isAutoStart = 'true', isAllow = 'false', reason = 'callee_does't_have_OP_AUTO_START_permission.': the firmware will not start the screensaver app's process to bind it. Allowing the op for that app only and saving it (tools/tv.py 'appops set <package> APP_AUTO_START allow; appops write-settings') fixed it, and it still bound after a reboot. Safety Guard (com.tcl.guard, disabled by the TCL lists here) is not the cause: enabling it again changed nothing. The launcher's own Clock screensaver is not affected: Home is already running, so binding ClockDream starts nothing (checked on the same TV, with the launcher's APP_AUTO_START at ignore). To test without waiting for the timeout: tools/tv.py 'am start -n com.android.systemui/.Somnambulator', then tools/tv.py 'dumpsys dreams | grep mCurrentDreamName'. The key helpers hit the same denial: see Accessibility helper enabled but the remote button does nothing.

  • On Android 14 the op is ASSOC_START, and AUTO_START alone is not enough (TCL Smart TV Pro, G10_4K_GB, Android 14, UTT2.250416.001). cmd appops set <package> APP_AUTO_START allow is refused on this firmware with Unknown operation string, and the denial for a content provider is the start association, not the start: with AUTO_START at allow and ASSOC_START at ignore, the firmware will not start the helper's process for the caller, so the provider query throws and a launcher that hides its row when the provider cannot be read shows nothing at all. The reading works from a shell (content query --uri content://<authority>/today prints a row) while the launcher's own query fails, which is what makes it look like an app bug. Allow both and save them:

    tools/tv.py 'appops set <package> AUTO_START allow; appops set <package> ASSOC_START allow; appops write-settings'
    

    appops write-settings matters here as it does for AUTO_START: app-ops reach the disk lazily and a plain reboot drops an unsaved grant. Check with tools/tv.py 'cmd appops get <package> ASSOC_START'. Verified on that set across reboots and reinstalls.

  • The TV wakes from standby back on the last HDMI input, and power_on_home takes about 5 s or misses it (Philips 65OLED706, Android TV 11): the firmware only resumes the org.droidtv.playtv window it left, which sends its window event once the input has a picture, if at all. Set power_on_home_always too: Home comes up about a second after the screen turns on, whatever is in front (an input, Live TV or an app).

  • BBC iPlayer, ITVX, Channel 4 and 5 vanished after the debloat (Sony KD-65XH9296, UK, BRAVIA_VH1, Android 12): com.youview.poa, the Freeview Play app behind the "Live TV & Catch Up" tile, runs them as web apps in Sony's HbbTV player. They are not packages of their own, so only that package leaves with it. pm enable com.youview.poa brought them back. How each one launches, and how to give each a Home tile, is in the Sony UK catch-up note.

Accessibility helper enabled but the remote button does nothing

The optional HomeKeyService and InputKeyService only filter remote keys once Android has bound to them. The installer's enabled message means the setting was written, not that the service is bound. Read the state without changing anything:

tools/tv.py 'am get-current-user'
tools/tv.py 'settings --user current get secure enabled_accessibility_services'
tools/tv.py 'settings --user current get secure accessibility_enabled'
tools/tv.py 'dumpsys accessibility'
tools/tv.py 'appops get com.gathin.tvhome'

For a bug report, give the model, Android version, build, current user and the relevant redacted lines from these outputs, not the raw dump, as the auto-start note at the end of this section asks.

Helper Component in the enabled-services list Key it filters
Home button com.gathin.tvhome/.HomeKeyService KEYCODE_ALL_APPS (an ordinary KEYCODE_HOME only with home_long_press_home, config.xml or a theme) and KEYCODE_SETTINGS, plus the keys in settings_key_keycodes and app_keys (config.xml or a theme) and in settings_key_scancodes (config.xml only)
Input button com.gathin.tvhome/.InputKeyService KEYCODE_TV, KEYCODE_TV_INPUT, plus the keycodes in input_key_keycodes and app_keys (config.xml or a theme)

A force stop of the launcher (Settings > Apps, a cleaner app, am force-stop) takes both helpers off that list. With restore_key_services on (config.xml or a theme) and WRITE_SECURE_SETTINGS granted (RESTORE_KEY_SERVICES=1 on every install, -RestoreKeyServices on Windows; an install without it takes the grant back), Home puts the helpers that were running back on the list the next time it starts; a helper turned off in Settings stays off.

  • The Settings key still opens the panel on Home, and does nothing anywhere else. That is the same force stop, not a broken panel: Home handles KEYCODE_SETTINGS itself while MainActivity is in front, so the key keeps working there with no helper bound, and only outside Home does it need one. The panel then looks half-broken — it opens over Home and never over an app, Live TV or Settings — which sends you reading QuickSettingsActivity instead of the helper list. Check the list before the code: it reads null after a force stop that took both helpers off, and these two writes put them back:

    tools/tv.py 'settings put secure enabled_accessibility_services com.gathin.tvhome/.HomeKeyService:com.gathin.tvhome/.InputKeyService'
    tools/tv.py 'settings put secure accessibility_enabled 1'
    

    Seen on a TCL Smart TV Pro (G10_4K_GB, Android 14, UTT2.250416.001), chasing a panel that had never been at fault.

  • Home puts the helper back on the list after a force stop, and it still is not bound. dumpsys accessibility then shows Bound services:{}, accessibility_enabled falls back to 0, and the helper sits under Crashed services: a killed helper is marked crashed, and Android never binds a crashed service again, whatever the list says. It drops the mark only when it sees the helper leave the list, and after a force stop the list is already empty, so a plain removal changes nothing. ServiceRepair therefore adds the helpers, removes them and adds them again, a second apart. Seen on a Hisense SmartTV 4K FFM (songshan, Android 11, RTT2.220118.001), whose standby (com.hisense.STR_SUSPEND) kills every third-party app except the one on screen (PowerVtImp: kill 3rd app): the remote's app buttons stopped after the first standby with another app open. By hand, the same three writes:

    tools/tv.py "settings put secure enabled_accessibility_services com.gathin.tvhome/.InputKeyService; sleep 1; settings put secure enabled_accessibility_services ''; sleep 1; settings put secure enabled_accessibility_services com.gathin.tvhome/.InputKeyService"
    
  • Enabled means the component is in that user's colon-separated list (the class may be written in full, com.gathin.tvhome/com.gathin.tvhome.HomeKeyService). Match the whole component.

  • Bound means it appears under that user's Bound services section of the dump (services on older builds). Give it a few seconds after enabling and read again. Labels in the dump can be translated or shared with other services; a line containing "Home" is not proof.

  • Working means the physical button does the right thing from inside another app. An injected key over ADB proves navigation, not that the remote sends the code this helper filters. It does not even reach the helper: input keyevent skips accessibility key filtering, so an injected KEYCODE_TV does nothing even when the real Input button works.

  • Which keycode a dead button sends: dumpsys window policy lists the "global keys" Android routes to one app (on Google TV several go to launcherx, which is disabled), and Xiaomi firmware logs every press as handleAction<keycode> in logcat. TCL Google TV firmware logs it as TclPhoneWindowManager: handleKeyBeforeQueueing, key = 4099, down = true (a TCL 55C728, Google TV 11); read the log straight after the press, as its main buffer there holds well under a minute. Put it in input_key_keycodes (opens Inputs) or app_keys (keycode:package, or hold:keycode:package for a long press), reinstall with INPUT_KEY_SERVICE=1 and press the real button. A press that logcat reports as KEY_UNKNOWN or KEY_PROGRAM cannot be matched by keycode at all, because every unmapped button shares those codes: use the Linux scancode from tools/tv.py 'getevent -lt' in settings_key_scancodes and install with HOME_KEY_SERVICE=1 (an RC-36 remote sends its gear on scancode 240 and its notifications button on 362 that way; see the FPD CG50-C3 note). Not every set answers those two routes, though (#853, a PEAQ "AI PONT DE", DVB99PQ / Changhong changi, Google TV 14, UKNV.260504.001): a HID consumer-control usage prints an evdev MSC_SCAN that is not necessarily the KeyEvent.getScanCode() the framework reports, so a value read with getevent -lt can match nothing in settings_key_scancodes (that set's gear printed MSC_SCAN 000c009f, and neither 159 nor 786591, the two spellings of it, did anything), and logcat -v time -s InputReader:D, the filter config.xml suggests, printed no key line at all there, for any button, only Reconfiguring input devices. What Android really delivers comes from a temporary Log.d at the top of the helper's onKeyEvent printing getKeyCode(), getScanCode() and getDeviceId(), rebuilt with HOME_KEY_SERVICE=1 and read with tools/tv.py "logcat -s TvHomeKeys:D" while pressing the real button: that set reported action=0 keyCode=83 scanCode=240 deviceId=9, and settings_key_keycodes = [83] opened the TV's Settings at once. So a getevent code that matches nothing is not proof the press never arrived, and consuming the key in the helper also keeps it from the vendor receiver com.homwee.factory/.FactoryKeyReciver, which dumpsys window policy routes KEYCODE_NOTIFICATION to once the stock launcher is gone.

  • All vendor buttons die at once and logcat says GLOBAL_BUTTON ... process is bad: the global-key receiver crashes on the disabled launcherx and Android stops starting it. Map every such keycode in the helpers, also for disabled apps. See the MOSR4 note.

  • A remote shortcut (such as a "Live" or TV button) opens Google Play Store or crashes after debloating: When a target app such as Live Channels (com.google.android.tv) is disabled, pressing its physical button makes OEM broadcast receivers (such as Xiaomi's com.xiaomi.android.tvsetup.partnercustomizer/.GlobalKeyReceiver) launch a market intent to install the missing package, or crash repeatedly. To remap it cleanly, put its hardware scancode in app_keys: "scan:<scancode>:<package>" (e.g. "scan:399:org.smarttube.stable" on the Xiaomi Mi Box S, read from getevent -lt) and rebuild with INPUT_KEY_SERVICE=1. Use the scancode form (scan:<scancode>) rather than a raw keycode (191): generic button keycodes like KEYCODE_BUTTON_4 (191) may be shared with other buttons on some remotes/key layouts, so mapping by keycode can unintentionally hijack other buttons. scan: checks match first and target only that specific physical button. See the Mi Box S note.

  • The Settings button opens Android TV Settings, not the TV's own menu (Sony BRAVIA VU3, Google TV 14, BRAVIA_VU3; the report records no build, #2131): the Quick Settings ribbon that button used to open (Picture mode, Picture settings, Brightness) is Sony's own app, and the helper takes KEYCODE_SETTINGS before any app is asked, so the press opens the standard settings action - on this set com.android.tv.settings/.MainSettings, or the launcher's own panel with settings_key_panel. With the helper off the press arrives and nothing opens: WindowManager logs mShortPressOnSettingsBehavior=SHORT_PRESS_SETTINGS_NOTHING. The ribbon is still installed and opens when it is asked directly: tools/tv.py 'am start -n com.sony.dtv.smartui/.settings.SmartSettingsActivity'. settings_key_keycodes and settings_key_scancodes do not bring the ribbon back on their own: both end in Settings.ACTION_SETTINGS, and settings_fallback_components is read only when that action resolves to nothing, which it does not on this set. If the remote reports a scancode for that button, an app_keys entry should put it back (read from the launcher's code, not yet tried on a set), because a scan: entry is answered before the settings branch in HomeKeyService.onKeyEvent: "scan:<scancode>:com.sony.dtv.smartui/.settings.SmartSettingsActivity" in config.xml, or in a theme's app_keys (find <scancode> with getevent -lt while pressing the button, as the scan: bullet above does for a Sony remote). A keycode entry cannot: "176:..." is KEYCODE_SETTINGS, which the helper takes itself before it looks at any app key.

  • The gear does nothing after the stock launcher is disabled (onn 4K Streaming Box, YOC, Google TV 14, URO4.260304.011.B1.15051976, and Hisense SmartTV 4K, lushan / his9603, Google TV 12, STT2.230929.001.092). The app buttons and the mic can keep working while this one does not: on the onn box com.gretzky.GlobalKey still has those, and the gear used to be answered by launcherx. Reinstall with HOME_KEY_SERVICE=1, set settings_key_panel (config.xml or a theme) and put 83 in settings_key_keycodes. 83 is KEYCODE_NOTIFICATION, the code the stock Google TV home turned into its quick settings panel. The gear then opens the launcher's panel (TV settings, Display, Network, Apps, Launcher settings) from Home or another app, and the helper was still bound after a reboot on both sets. Where InputKeyService is already bound for the app buttons, the same key needs no second helper: 83:settings in app_keys opens the TV's Settings app from Home and from inside other apps, and settings_key_keycodes can stay as the Home-only fallback. That route is quicker and needs no rebuild with HOME_KEY_SERVICE=1, but it opens the Settings app rather than the launcher's panel; verified on a TCL 55Q7D Pro (G08_4K_GB, Google TV 14, UTT2.250416.001 AU02), in SmartTube and on Home. Press the real button; a key injected over ADB does not reach the helper. Holding volume may step only once while the helper is bound, which is the usual trade on these two boards.

  • The list still names the helper but accessibility_enabled is 0, so nothing is bound and the button does nothing (not the force stop below): on a TCL C6K Pro (G08, Android 14, #1047) a reboot left enabled_accessibility_services naming com.gathin.tvhome/com.gathin.tvhome.HomeKeyService while accessibility_enabled read 0, dumpsys accessibility showed Bound services:{} and the Home button opened Google TV. restore_key_services does not repair this state: ServiceRepair writes accessibility_enabled 1 only on the path that also puts a missing helper back on the list, and here the list was complete. Write the flag back, then read the dump again; toggling the service off and on in Accessibility, which is what the owner did, brought the redirect back for that session. Whether that manual flag change survived the next reboot was not retested; the cold-boot follow-up below tests the fork's separate opt-in startup repair. In one cold-boot test of that repair, TCL had reset AUTO_START to ignore; the boot log showed the firmware blocking both the accessibility-service bind and the boot receiver. After allowing AUTO_START for com.gathin.tvhome and persisting app-op settings with appops write-settings, the next cold boot left accessibility_enabled at 1, HomeKeyService bound, and CleanYourTV in the foreground about eight seconds after Google TV started. The owner confirmed this one run; it does not establish that the upstream launcher starts at boot or guarantee every reboot. Google TV was left enabled. What first reset either setting is not established. On a TCL 65C655 Pro (G03, Android 11, examples/tcl-55c725) every one of three reboots left the same state, and there writing the flag back alone bound nothing; clearing enabled_accessibility_services and writing it back, as build-and-install does, did:

    tools/tv.py 'settings get secure accessibility_enabled'
    tools/tv.py 'settings put secure accessibility_enabled 1'
    tools/tv.py 'dumpsys accessibility'
    

A force-stop switches the helpers off for good: after am force-stop com.gathin.tvhome (or Force stop in Settings > Apps) Android removes the launcher's services from the enabled list, and they do not come back when the launcher starts again (seen on a TCL 55C715, Android TV 9). Enable them again the same way you did the first time, and avoid force-stopping the launcher while testing.

Enabled but never bound points at a firmware restriction on auto-starting sideloaded services (some TCL builds log OP_AUTO_START denials). Look at logcat for the binding failure before changing settings, and do not rewrite the enabled-services list or grant app-ops as a blanket fix. On a TCL 55S470G (Google TV 12) the line is [TclAppBoot] ... calleeComponent = 'com.gathin.tvhome/.HomeKeyService', bootType = 'bind_service', isAllow = 'false', reason = 'callee_does't_have_OP_AUTO_START_permission.', and appops get com.gathin.tvhome shows APP_AUTO_START: ignore. Allowing that one op for the launcher only and saving it (appops set com.gathin.tvhome APP_AUTO_START allow; appops write-settings) made the service bind on its own after a restart; toggling the service off and on in Accessibility works until the next restart. App-ops reach the disk lazily: without write-settings, a restart soon after the set drops the change and the denial is back. Seen on a TCL C89B (Google TV 14, G08, UTT2.250416.001 AU02), whose log reads [TclAppBoot]:forbid bind service ComponentInfo{com.gathin.tvhome/com.gathin.tvhome.HomeKeyService} and whose appops get names the op AUTO_START: use the name appops get prints. When reporting, give the model, build, current user and the relevant redacted lines, not the raw dump.

On a TCL Smart TV Pro (G05, Android 11, AR2101), the same permission was set through Safety Guard → Permission Shield → Auto Launch Permission. Temporarily set Auto manager to Closed, set the reference launcher's entry to Opened, then restore the original Auto manager setting. After a reboot, APP_AUTO_START read allow and HomeKeyService was bound without an ADB repair. See the G05 startup-permission check for the steps, verification and undo; this has not been retested on the G03 or G08 units above.

SHIELD TV phone app connects, but no key does anything

On an NVIDIA SHIELD, the SHIELD TV phone app finds the box and connects, but the D-pad, OK, Back and power buttons in the app do nothing. Restarting the remote server brings them back until the next reboot. The key helpers are not the cause: it happens with them off too.

The app's keys go through com.nvidia.shield.remote.server, which injects them through its own virtualinput.InputService, a TvRemoteProvider that Android's TV remote service binds at boot. Read the state without changing anything:

tools/tv.py "logcat -d | grep -E 'Key injection failed|Beyonder_VIS'"
tools/tv.py "dumpsys activity services com.nvidia.shield.remote.server | awk '/\* ServiceRecord/{inb=/virtualinput.InputService/} inb' | grep -c IntentBindRecord"

Key injection failed with Beyonder_VIS ... mProvider ... isInitialized=false, and a count of 0, is this fault: the service runs but Android never bound it. 1 is a bound, working service.

Cause: com.google.android.tvrecommendations disabled. With it disabled, the provider is not bound after boot. Bisected with a reboot per step on a 2017 SHIELD (darcy) and confirmed on a 2015/2017 one (foster_e), both on SHIELD Experience with Android 11: every reboot with it off left the count at 0, every reboot with it on gave 1. Fix:

tools/tv.py "pm enable com.google.android.tvrecommendations"

Wait about 20 seconds before rebooting: Android writes package state to disk with a delay, and a reboot straight after pm enable can lose the change. am force-stop com.nvidia.shield.remote.server makes Android bind the provider at once, but only until the next boot. The SHIELD lists in examples/ keep tvrecommendations enabled for this reason.

Fire TV (Fire OS): debloat works, the launcher cannot take Home

Why Google TV differs: on Google TV a third-party app can take the HOME role, so the launcher, the set-home-activity default and the stock-launcher disable all work. Fire OS keeps Home for Amazon's own launcher, and the same tools fail there. Checked on an Amazon Fire TV, model AFTDEC012E (codename ginza), Android 11 (SDK 30), build RS8185.3885N:

  • cmd package query-activities for HOME: com.amazon.tv.launcher/.ui.HomeActivity_vNext at priority 950, com.amazon.firehomestarter at 1. A third-party launcher installed with android:priority="1000" on its HOME filter still sits at 0. Fire OS ignores the priority of a non-system app.
  • cmd package set-home-activity com.gathin.tvhome/.MainActivity prints Success, and HOME still resolves to Amazon's launcher.
  • pm disable-user --user 0 on com.amazon.tv.launcher, on its component HomeActivity_vNext, and on the other Fire OS home packages is refused: Cannot disable a protected package.
  • Intercepting the Home key with an AccessibilityService did not work: onKeyEvent received nothing for injected keys (input keyevent KEYCODE_HOME) or for a 40-second window of remote presses.

Debloat: tools/disable.sh examples/amazon-fire-tv-aftdec012e/debloat.txt disables telemetry, ACR, shopping and tutorial packages. Undo with tools/restore.sh examples/amazon-fire-tv-aftdec012e/debloat.txt com.amazon.tv.launcher.

The undo prints one Cannot disable a protected package stack trace. tools/restore.sh enables every package the recovery record holds as on, and the record also holds the package that owned Home, because putting Home back is what undo is for - on Fire OS that is com.amazon.tv.launcher. Fire OS protects that package in both directions, so the pm enable com.amazon.tv.launcher line in the batch answers java.lang.SecurityException: Cannot disable a protected package: com.amazon.tv.launcher with a stack trace, once per run. Nothing failed: the package was already on, tv.py --batch carries on and sends every command after it, and the run still exits 0. Leaving com.amazon.tv.launcher off the restore.sh command line does not remove the line - the record puts it back. Reported on an AFTKM (karat, Fire OS 8.1.8.5, build RS8185/3898) replaying five recovery records after an OTA (#2950).

Home and the Alexa button do nothing, every other button works, seen on a Fire TV Stick 4K 1st gen (AFTMM, mantis, Fire OS 6.7.1.1, build NS6711). It was there before any debloat and survived a factory reset, so check it before blaming a list. The remote is fine: getevent -lt shows KEY_HOMEPAGE and KEY_SEARCH arriving from "Amazon Fire TV Remote", and logcat shows Fire OS receiving them (mic_button_released, handleLongPressOnHome), yet Home stays put and Alexa never listens. The cause was settings get secure user_setup_complete answering 0 (with global device_provisioned at 1 and the Amazon account registered): the first-run setup had been left unfinished, and Android holds back Home and the assistant until it is. settings put secure user_setup_complete 1 brought both buttons back; 0 undoes it. A factory reset clears it again, so finish the setup wizard to the end afterwards.

Remapping the remote's app buttons without a key helper

A process of your own can take the buttons the helpers exist for without binding an accessibility service, and the key auto-repeat a helper costs stays: a key-filtering service pushes every key through a filter path that drops the repeats, while reading the remote's input device leaves Android's input alone. It has to run as the shell user, because a normal app cannot open /dev/input/event*, so this is not something the launcher can grow into — it is a script and a supervisor on the TV. On a Google TV Streamer (kirkwood, Google TV 14, UTTK.260317.003) that was the one way to have both: HomeKeyService alone, InputKeyService alone and both together each stopped the ramp after roughly six steps, and it came back the moment the service was unbound (#796).

The Streamer's remote appears as Google TV Remote Keyboard, and these are the buttons it sends:

Button HID usage Android keycode
YouTube 0x000c0077 BUTTON_3 (190)
Star 0x000c0186 MACRO_1 (313)
Netflix 0x000c0078 BUTTON_4

Find the node by name, not by number: tools/tv.py 'getevent -pl' lists every input device with its name and the key codes it declares, and the /dev/input/event<N> a remote lands on is not stable between boots, so a watcher pinned to event5 stops working after the next reboot and reads as a dead button again.

A reader cannot consume the press. getevent observes the events and Android still delivers them to whatever answers them today, so a remapped button's stock action runs as well: map the Netflix button and Netflix starts too, then sits in the activity back stack. A helper consumes the key it maps — which is why the vendor receiver in the "Which keycode" bullet above never sees it — so the buttons you have to suppress, rather than duplicate, still need a helper and its repeat trade.

What runs it again after a reboot is the part that is not free. The shell user reads /dev/input/eventX on this unit without root and a script kept under /data/local/tmp/ runs, but nothing there starts itself: Android gives the shell user no cron and no init hook without root, so the watcher is gone after every reboot and has to be started again from a debugging session you close afterwards. Two things fail silently when you do: the script must be detached from the adb shell session or it dies with it, and a supervisor must not start a second copy. Do not solve the reboot by leaving TCP ADB on so something local can restart it — port 5555 reachable from the LAN is a shell on the TV for anything on the network, which is why the closing step in README turns it off.

The Netflix button still opens Netflix, and a key helper never sees it (TCL UnionTV)

Seen on: a TCL UnionTV (ro.product.model UnionTV, Android 9, PPR2.180905.006.A1, remote TCL_RC802VEM) with the launcher as Home and InputKeyService and HomeKeyService both enabled.

The Netflix button sends keycode 4062 (scancode 0, deviceId=-1). With 4062:<package> in app_keys the button still opened Netflix and the mapped app never started. logcat shows why:

  • interceptKeyTq keycode=4062 is logged, then an ActivityManager START ... com.netflix.ninja/.MainActivity about 60 ms later from Netflix's own uid. The firmware sends the key to Netflix's NetflixKeyReceiver (action com.netflix.ninja.intent.action.NETFLIX_KEY, a signature|privileged permission), and Netflix starts itself before any window gets the key.
  • pm disable com.netflix.ninja/.NetflixKeyReceiver is refused for the shell user (Shell cannot change component state). Disabling the whole app works: pm disable-user --user 0 com.netflix.ninja (undo with pm enable com.netflix.ninja). Do that only if the person does not use Netflix.
  • With Netflix disabled the button does nothing, and logcat shows Unhandled key: win=Window{... com.gathin.tvhome/...MainActivity}, keyCode=4062: the key reaches the focused window, but the two accessibility helpers still never get it, so app_keys has no effect. The firmware appears to inject this key after the input filter the helpers sit on.

So on this set only the focused window can answer the button, and only while Home is in front. Find the keycode first (tools/tv.py 'logcat -d -v time | grep interceptKeyTq' right after pressing the button), because getevent -lt did not show it in the captures taken here.

The TV button does nothing and no app ever sees the key (TCL Google TV)

Seen on: a TCL 55Q7D Pro (model "Smart TV Pro", G08_4K_GB, Google TV 14, firmware UTT2.250416.001 AU02, remote BT_RC833A_B5) with com.google.android.apps.tv.launcherx disabled and the launcher here as Home, InputKeyService bound.

Pressing the TV icon did nothing at all: topResumedActivity stayed on Home, no input list opened, and no VIEW_INPUTS intent was sent. getevent shows the press as KEY_UNKNOWN (the remote reports scancode 240 for every key) with usageCode 0x000c005b; TclKeyLayoutMap maps that to keycode 4001. The key never reaches a window, because TCL's own shortcut manager inside system_server takes it before dispatch:

I/TclShortCutKeyManager: handle custom key in press down:4001
I/TclShortCutKeyManager: start PackageName:null
W/ContextImpl: Calling a method in the system process without a qualified user:
    android.app.ContextImpl.startForegroundService:1917
    com.android.server.policy.TclShortCutKeyManager.handleKeyBehavior:693
    com.android.server.policy.TclShortCutKeyManager.handleShortCutKeyProto:572
I/TclShortCutKeyManager: not continue dispatch
D/WindowManager: handleKeyBeforeDispatching keyCode=4001 disable

start PackageName:null is the whole problem: the package the shortcut should start is empty on this build, so nothing happens and no other component is told. The TV app is not the culprit — am start -n com.tcl.tv/com.tcl.player.TVActivity shows Live TV, and its input is com.tcl.channelplus/com.tcl.iptv.service.InputService (label "Live TV"), which dumpsys tv_input lists with a session owned by com.tcl.tv. Do that first: it separates a broken Live TV app from a key that never arrives.

Fix: with the helper's key filtering left at its default — the switch itself, and what it costs, are in A key helper is bound and a held remote button stopped repeating — the accessibility helper still receives the key before the policy consumes it, so map it in app_keys. The target has to be a package/.Activity entry, because com.tcl.tv's leanback entry is not TVActivity:

"app_keys": ["4001:com.tcl.tv/com.tcl.player.TVActivity"]

That needs InputKeyService (INPUT_KEY_SERVICE=1), and the button then opens Live TV from any app. Verified with the real remote. An input:com.tcl.channelplus/com.tcl.iptv.service.InputService target also works when live_tv_package is set, but the direct activity is what was tested here. Find the keycode of a button like this one with tools/tv.py 'logcat -d -v time | grep TclKeyLayoutMap' while pressing it — getevent alone shows every TCL remote key as KEY_UNKNOWN.

The Inputs list shows the empty HDMI 1 as connected, and a device on HDMI 2 nowhere (TCL UnionTV)

Seen on: a TCL UnionTV (ro.product.model UnionTV, Android 9, PPR2.180905.006.A1), with the launcher as Home and its default config.

The firmware puts each HDMI port's state on the wrong port: the "HDMI 1" input always reports connected, the "HDMI 2" input follows the physical HDMI 1, and a device on the physical HDMI 2 changes nothing. The CEC device list stays right (a Steam Deck on HDMI 2 is reported on port 2). The likely cause is that the firmware numbers hdmi_port from 0 while CEC numbers ports from 1. The shell cannot fix it: the mapping lives in TCL's TV input service on a verified, signed partition.

Fix: inputs_cec_state_only true in config.xml and rebuild, or "inputs_cec_state_only": true in a theme's config.json. Bare HDMI ports then show no state and only CEC device rows show theirs ("Steam Deck · HDMI 2", Connected). The trade: a device without CEC, such as a laptop, shows no state either. The switch and its screenshot: docs/switches.md.

Every clock stops after the power button, until a reboot (TCL UnionTV)

Seen on: a TCL UnionTV (ro.product.model UnionTV, Android 9, PPR2.180905.006.A1).

The clocks freeze at the minute the TV was last put to sleep with the power button, while the system time stays right. TCL's AlarmManager skips new alarms while sys.tcl.powerstatus reads suspend (logcat: sys.tcl.powerstatus is suspend, skip setImplLocked), and on waking it drops the one that sends ACTION_TIME_TICK: dumpsys alarm | grep mLastTick shows the last tick at the minute of the power press. Before Android 12 a TextClock only moves on that tick.

The launcher redraws its own clocks every minute, and when the display comes back on, on Android 11 and older, so Home and the Clock screensaver keep time. Clocks in other apps stay frozen. Unplugging the TV, or setting the time once, arms the tick again until the next power press: tools/tv.py "cmd alarm set-time $(( $(date +%s) * 1000 ))" (the sum is done on the computer: the TV's shell overflows it).

A key helper is bound and a held remote button stopped repeating

Hold volume up or down and the volume moves one step per press instead of ramping. The remote may be fine and so may the debloat: the ramp is back the moment no key-filtering accessibility service is bound, and getevent -lt shows whether the repeat still arrives from the remote while the button is held. On a TCL 55P71B Pro (G09, Google TV 12, STT2.230203.001.AS23, #763) it was all or nothing - HomeKeyService alone, InputKeyService alone and both together all broke it, with settings put secure enabled_accessibility_services '' plus accessibility_enabled 0 restoring it. The dumps show capabilities=8 (CAPABILITY_CAN_FILTER_KEY_EVENTS), so the platform routes every key through a filter path that drops the repeats, before an app sees them; the launcher's own helpers return false for keys they do not map, so they are not consuming the volume key. The same was reported on a Xiaomi MiTV-MOSR4 (irobot, Android 11, RTM5.220609.003.1265) with the launcher's own helpers, both and InputKeyService alone (#944), where the empty list was not tried because it also unhooks the buttons the helpers were installed for. A Xiaomi Mi TV 4A (MiTV-MOOQ1, tarzan, MediaTek, Android TV 10, QTG3.200305.006.6148, #2446) does the same with InputKeyService alone, and the empty list was run there: holding Volume Up ramped again with accessibility_enabled 0, and the helper written back stepped it once more. On that set the helper is also what answers Input, because the firmware's GlobalKey starts VIEW_INPUTS with the component com.google.android.tvlauncher/.inputs.InputsPanelActivity written into it, so once launcherx is gone that press is dead; INPUT_KEY_SERVICE=1 with handle_view_inputs brings the button back by consuming the Input key and starting a new component-free VIEW_INPUTS intent, not by answering the firmware's explicit one. An onn 4K Streaming Box (YOC, Google TV 14, URO4.260304.011.B1.15051976) does the same with HomeKeyService alone; there key_filter false (config.xml or a theme) brought the repeat back with the helper still bound, so a helper kept for something other than keys need not cost it. A Hisense 75U75N (hengshan, Google TV 12, STT2.230310.002) with HomeKeyService alone does it too, and its log shows where the repeat goes: the filter hands volume keys to the window policy first, Hisense's policy sends the CEC step and consumes the key, so no helper sees it and nothing repeats (the G09 note has the log). The soundbar is not the condition: that unit had one, while a 55U7N on the same board and build steps once per press with HomeKeyService alone and no soundbar in its report (#1326), so the CEC step is one unit's account of a key the policy takes, not the cause. The 55U7N also shows where that board's app buttons go: with launcherx disabled Hisense's own GlobalKeyReceiver receives 295, 607 and 606 and nothing opens, so only a helper answers them (the hengshan notes); its held-key capture was not taken. A TCL 55Q77K (G08_4K_US, Google TV 14, UTT2.250416.001 AU16, TV speakers, no soundbar) steps once per press with InputKeyService alone as well. On that set Settings (83), Netflix (4062), Prime Video (4099) and Apple TV (4160) still reach the focused window with launcherx disabled, so Home could answer them without a helper, while YouTube is BUTTON_3, a global key Android hands to launcherx before any window (the G08 US notes). Check the state first, do not rewrite it blind:

tools/tv.py 'settings get secure enabled_accessibility_services'
tools/tv.py 'settings get secure accessibility_enabled'
tools/tv.py 'dumpsys accessibility'

This is a trade, not a defect to fix: the buttons the helpers exist for (a gear button sending KEYCODE_NOTIFICATION 83, app buttons sending vendor keycodes such as 4062) were answered by the stock home, so on a set whose list disabled launcherx only a helper can answer them. Either keep the repeat and install without INPUT_KEY_SERVICE=1/HOME_KEY_SERVICE=1, or keep the buttons and accept one step per press, or leave launcherx enabled and bind nothing, or keep the services bound and stop them filtering: "key_filter": false in a theme's config.json — the same bool in config.xml for a rebuild — makes them drop FLAG_REQUEST_FILTER_KEY_EVENTS at runtime, so the ramp comes back and the buttons they handled go dead, the same trade without a settings write. Write back the exact string the first read printed; the empty list also unbinds TalkBack. Bisect, keycodes and limits: the G09 note.

Where launcherx is what answers the buttons, keeping it enabled and redirecting its home screen keeps both. On a Sony BRAVIA VU3 (BRAVIA_VU3_4K, Google TV 14, UKR1.240726.001) the Netflix, YouTube, Prime Video and YouTube Music buttons are global keys (BUTTON_4, BUTTON_14, BUTTON_2, BUTTON_15) routed to com.realtek.advancesetting, which on a Google TV hands each launch to launcherx's GlobalButtonLaunch_Receiver and drops it when launcherx is disabled. InputKeyService with their scancodes in app_keys opened all four but cost the ramp, and the volume key never reached the helper, so consume_volume_keys could not repeat it. launcherx enabled, stock_home_redirect set to it, key_filter false and HomeKeyService bound gave the buttons, the ramp and the new Home together (the VU3 notes).

A Sony KD-55X9000H PA-firmware follow-up on Android 12 reproduced the same filter/repeat conflict with a personal fork. Its simultaneous kernel/callback capture saw held Volume in hardware but neither Volume direction in the helper. A separate saved-layout experiment retained Apps and native Volume without key filtering; it requires that fork's companion and Library entry, reserves an application-launch category, and has not been tested across restart/sleep/reconnect. It is not a stock upstream option.

The same setup works on a Google TV Streamer (YouTube is a global key to launcherx there) and on a Philips Google TV TA5, with one more step on the Philips: see the next entry.

App buttons stopped working after launcherx was re-enabled for the redirect

  • Netflix, YouTube and Prime Video did nothing once launcherx was enabled again, while they worked with it disabled. Seen on a Philips Google TV TA5 (PH1M_WW_9972, Google TV 14, UKN2.241117.001): with launcherx enabled, org.droidtv.GlobalKey hands those launches to launcherx, and Android blocks them as background activity starts while this launcher is in front. logcat shows START u0 ... com.netflix.ninja ... from uid <launcherx> (BAL_BLOCK) result code=102. Allowing launcherx to draw over apps exempts it: appops set com.google.android.apps.tv.launcherx SYSTEM_ALERT_WINDOW allow brought all three back, and it stays after a reboot. Undo with appops set ... SYSTEM_ALERT_WINDOW default.
  • A button that sends android.intent.action.ALL_APPS now shows a chooser, because launcherx answers the action too. Pick the one you want with "Always" once.

The Bluetooth Pair screen ignores OK while a key helper is bound

Pairing a headset or remote: Settings > Remotes & Accessories > Pair accessory opens the Pair/Cancel screen (com.android.tv.settings/.accessories.BluetoothPairingDialog), "Pair" looks selected, and OK, Up and Down do nothing. Press Right, then OK, and it pairs; with no key at all the bond times out after about 30 s (bondStateChangeCallback: Status: 9 ... newState: 0). Seen on a Polaroid AI PONT (Changhong redwood, MediaTek mt5867, Android TV 11, RTMA.250416.071, #3155) with HomeKeyService bound for power_on_home: getevent -lt shows the presses reaching the TV, and with settings put secure enabled_accessibility_services '' plus accessibility_enabled 0 OK pairs at once. Neither OK nor the D-pad is mapped in config.xml, so it looks like the same family as a held button that stopped repeating (#763, #944): a bound key-filtering service (capabilities=8) changes key or focus delivery on this firmware even for keys the helper returns false for.

Workaround: press Right on the Pair screen first, then OK. Or switch the helper off while pairing and back on afterwards (note what tools/tv.py 'settings get secure enabled_accessibility_services' prints first, and put that value back with settings put).

App buttons crash the TV's own key service on a set with a second user (Skyworth/Coocaa)

Seen on: a Coocaa Y74 Pro 65" that reports Skyworth SWTV-24AE-4K, product SW10_TG, device eleonas (MediaTek MT5896), Google TV 14, build UKN4.250714.140 - the board and product of the Motorola SWTV-24AE-4K list.

With launcherx disabled, the remote's gear and Input hotkeys killed Skyworth's persistent key service com.smartdevice.global.service (/system_ext/app/TvGlobalService/TvGlobalService.apk, flags=[ SYSTEM HAS_CODE PERSISTENT ALLOW_CLEAR_USER_DATA ]) with a NullPointerException on every press. A few presses later RescueParty reached RESET_SETTINGS_UNTRUSTED_DEFAULTS, which switched the launcher's accessibility helpers off; the levels after that are a reboot and then a factory-reset prompt. Re-enabling launcherx stopped it at once.

The cause is SkwGlobalKeyReceiver's KidMode check, x1.a.c(): when more than one Android user exists (UserManager.getUsers().size() > 1), setup is complete and the TV is not in offline mode, it takes a content provider client for launcherx's LiveTvContentProvider and queries it. With launcherx disabled that client is null, and only RemoteException is caught, so the receiver throws. logcat has all of it:

E ActivityThread: Failed to find provider info for com.google.android.apps.tv.launcherx.live.data.contentprovider.LiveTvContentProvider
E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.Cursor android.content.ContentProviderClient.query(...)' on a null object reference
E AndroidRuntime:   at x1.a.c(Unknown Source:150)
E AndroidRuntime:   at com.smartdevice.global.service.receiver.SkwGlobalKeyReceiver$a.c(Unknown Source:231)
W RescueParty: Attempting rescue level RESET_SETTINGS_UNTRUSTED_DEFAULTS

That second user is what decides it: the unit that hit this had a leftover Google TV profile (UserInfo{10:new_user}, no accounts) from changing Google accounts during setup, which the report suggests is why the same board in examples/motorola-swtv-24ae-4k was fine. A dormant profile is common enough - several lists in examples/ record the same new_user - so before launcherx goes on one of these boards, read the users:

TV_HOST=<tv ip> tools/venv/bin/python -I tools/tv.py 'pm list users'

If it has already happened, pm enable --user 0 com.google.android.apps.tv.launcherx brings the hotkeys back, and the helpers RescueParty turned off are the case in Accessibility helper enabled but the remote button does nothing.

One unit's logcat on one build: no other board is reported yet.

A toast at every power-on, and the system will not let you block it

  • "Switching to audio system. We recommend using an Ultra High Speed HDMI cable." for two seconds at every power-on, with a soundbar on HDMI eARC, whatever the cable. Seen on a Philips Google TV TA5 (PH1M_WW_9972, Google TV 14, UKN2.241117.001). It comes from org.droidtv.tv.tpvaudiomanager, whose notification importance is fixed (fixedImportance=true in dumpsys notification), so its channel cannot be turned off; appops set <pkg> POST_NOTIFICATION ignore and Do Not Disturb did not stop it either. On Google TV launcherx draws such toasts. Taking it out of android.app.role.SYSTEM_TELEVISION_NOTIFICATION_HANDLER (cmd role remove-role-holder) hid the toast until the next boot, when the role came back. Revoking launcherx's notification listener stayed across reboots:

    TV_HOST=<tv ip> tools/venv/bin/python -I tools/tv.py 'cmd notification disallow_listener com.google.android.apps.tv.launcherx/com.google.android.apps.tv.launcherx.coreservices.notificationlistener.TvNotificationListenerService'
    

    Every other on-screen notification from Google TV goes with it. Undo with allow_listener and the same component. With launcherx disabled for this launcher, check first whether the toast appears at all.

The power key does not wake the TV while the key helper is bound

A remote's power button does nothing while the TV is in standby, and only the set's own button wakes it. Seen on a TCL tc8000_ay30a2 (Android 11, RQ3A.210705.001.2024101221, #1387), where a key-filtering accessibility service that stays bound with the screen off was reported to swallow the power key. The launcher can stand its own helper down for standby instead of losing the helper altogether: with HOME_KEY_SERVICE=1 at install, at screen-off HomeKeyService takes itself off the accessibility list and the first screen-on puts it back, so the keys it answers still work whenever the screen is on. Off by default; three writes turn it on, and none of them is part of an ordinary install:

tools/tv.py 'pm grant com.gathin.tvhome android.permission.WRITE_SECURE_SETTINGS'
tools/tv.py 'cmd notification allow_listener com.gathin.tvhome/.GuardRevival'
tools/tv.py 'am broadcast -a com.gathin.tvhome.GUARD -n com.gathin.tvhome/.GuardConfig --ez guard_off_in_standby true'
# off again:
tools/tv.py 'am broadcast -a com.gathin.tvhome.GUARD -n com.gathin.tvhome/.GuardConfig --ez guard_off_in_standby false'

The broadcast prints guard_off_in_standby=true back, so the answer is readable without a reboot. The first two writes are what make the opt-in survive, so read why:

  • WRITE_SECURE_SETTINGS is what lets the launcher write the helper back onto the list at screen-on. Without it GuardKeep gives up silently and the helper stays off after the first standby, which looks like the helper having died. RESTORE_KEY_SERVICES=1 at install (-RestoreKeyServices on Windows) already grants it (the force-stop section), and an install without it takes the grant back, the one made by hand above included, so once the opt-in is on, pass it on every reinstall.
  • GuardRevival is the listener the system re-binds once the process is gone, and firmware that kills third-party apps in standby takes the screen-on watch with it. The rebind is what re-arms it.

While the opt-in is on, dumpsys accessibility naming no com.gathin.tvhome/.HomeKeyService during standby is the opt-in working, not a fault: wake the TV and read it again. Only that helper stands down; InputKeyService keeps filtering, so the Input button still works.

A power button that does nothing at all while the TV is on, with a long press opening the shutdown dialog instead, is a different fault: a TCL 43BP615 whose stock launcher switched the TV off on one press.

A power press that does nothing and a long press opens the shutdown dialog

A press on the remote's power button does nothing, and a long press brings up Android's shutdown confirmation instead of turning the set off, where the stock launcher switched it off on one press. Checked on a TCL 43BP615 (BeyondTV, Android 11, SDK 30, #1608) after an ordinary launcher/build-and-install.sh with no key helper flags, and again with HomeKeyService bound through settings put secure enabled_accessibility_services: the dialog came up both times, so the helper is not what changes the key. The owner's reading is that the platform takes KEYCODE_POWER itself, in the window policy, before an application or an accessibility service is handed it, and the launcher maps no power key of its own (its only KEYCODE_POWER mention keeps the key out of the power_on_home handover) — so there is no app_keys entry, theme key or config.xml value that takes the press back. On this set the one-press power-off belonged to the firmware's own home screen and went with it.

Nothing is broken: the install, the debloat and the key helper are not involved, and the shutdown dialog is Android's, so the TV still turns off from it. An owner who wants the one-press power-off back gets it by putting the stock launcher back — tools/restore.sh <list> <stock launcher> — and hands Home back to it at the same time.

The soundbar on ARC/eARC falls asleep and the TV does not wake it

A soundbar on the TV's ARC or eARC port can go to standby on its own after a stretch of silence while the TV stays on, and starting an app or a video does not bring it back until someone wakes it by hand. Seen on a Sony BRAVIA 4K VH2 (KD-55X80J, Google TV 12, STT2.230505.001.S136) with a Moto 160SB on HDMI 3 (eARC), with the TV's own settings already right (Speakers: Audio system, Audio system prioritisation on, BRAVIA Sync on).

The TV reports the state over ADB. While the soundbar sleeps, both values are false:

tools/tv.py 'dumpsys hdmi_control | grep -E "mSystemAudioActivated|mArcEstablished"'

Asking for System Audio Mode over HDMI-CEC wakes it, the same request the TV's Speakers → Audio system setting sends; the soundbar reconnects about 5 to 8 seconds later:

tools/tv.py 'cmd hdmi_control setsam on'

To have that happen without a computer, an app needs the shell: on Android 12 an app cannot send CEC itself, and writing the TV's speaker setting needs the maker's own permissions. These were tried on the VH2 and do nothing from an app: Android's system-audio setting, volume nudges, and Sony's audio service. What does work is an app that keeps Network debugging on and connects to the TV's own ADB on 127.0.0.1:5555 with its own key, which the TV asks to allow once, and runs only that one command when an app opens or media starts while no TYPE_HDMI_ARC/TYPE_HDMI_EARC output is listed. The Sony BRAVIA Launcher in SHOWCASE.md does this (SoundbarWaker.java). It is a trade-off: Network debugging stays on and the port is open on the home network, which the PROMPT's last step closes on purpose; on a set with ro.adb.secure=0 an open port is a shell for anyone on the network, so only consider it where the TV asks to allow each new key. On Sony, an app can instead open the Home Theatre Control panel (com.sony.dtv.HomeTheatreControl) for about half a second, which also wakes the soundbar and needs no ADB, at the cost of the panel flashing on screen.

The soundbar stays silent after a console wakes the TV, and only unplugging the bar fixes it

Sound stays on the TV's speakers when a console turns the TV on, and nothing on the TV brings the soundbar back. Seen on a Philips 70PUS8545/12 (TPM191E, PH9M_EA_5599, Android TV 12, STT2.230526.001) with a Philips TAB5305 soundbar on HDMI 3 (ARC) and a Nintendo Switch 2 on HDMI 1; the owner had it from the start, before any debloat. mArcEstablished and mSystemAudioActivated are false, as in the section above, but here the TV cannot send anything on the CEC line at all. The message history and the log show it, and both are lost at a restart, so read them first:

tools/tv.py 'dumpsys hdmi_control' | grep -E "mIsCecAvailable|mArcEstablished|\[[SRH]\]" | tail -20
tools/tv.py 'logcat -d' | grep -E "Tx notify NOT coming|EnqueueTx|Failed to send" | tail -5

In the capture, the Switch's <Image View On> and the soundbar's <Set System Audio Mode> (on) arrived within 200 ms of each other as the TV woke. Three seconds later every message the TV sent failed (CEC_HAL: Tx notify NOT coming, Timeout, Failed to send <Report Physical Address> ... with errorCode=2), after a minute the queue itself refused (MTCEC_EnqueueTx() fail, send_msg Enqeue Fail ret BUSY), nothing more was received, the CEC device list stayed empty and mIsCecAvailable read false. Turning CEC off and on with settings put global hdmi_control_enabled 0 then 1 did not clear it, and neither did unplugging the Switch's HDMI cable. Cutting the soundbar's mains power did, at once: 17 seconds later the TV was polling and talking to the Switch again, so the soundbar was the one holding the line. Plugged back in, it turned itself on and set up ARC on its own. cmd hdmi_control setsam on from the section above was not tried; it has to go out on the same line.

So when it happens, unplug the soundbar from the mains for about ten seconds; the TV needs no restart. To keep the two messages from colliding, the Switch 2's option to turn the TV on can be switched off and the TV turned on with its own remote first. That avoidance comes from one capture, and no TAB5305 firmware fix was found. Not checked: other soundbars and consoles, and other TV boards.

Moonlight: two controllers both drive player 1, or a DualSense button opens the exit prompt

With HomeKeyService bound (HOME_KEY_SERVICE=1) and filtering keys, Moonlight streaming from Sunshine on a TCL G07 (G07_4K_GB_NF, Android 11, AR17) with a DualSense (054c:0ce6) and an Xbox Wireless Controller (045e:0b20), both over Bluetooth, put both controllers on player 1, and Cross on the DualSense brought up Moonlight's exit prompt instead of pressing A. Both controllers were listed in dumpsys input as separate devices with sources 0x01000511, so pairing was not the problem.

The DualSense half has a known shape: Android 11 ships no key layout for it, so dumpsys input shows KeyLayoutFile: /system/usr/keylayout/Generic.kl, and getevent -lp shows the generic HID order (BTN_GAMEPAD, BTN_EAST, BTN_C, BTN_NORTH, ...). Cross is BTN_EAST, which Generic.kl maps to BUTTON_B, and an unhandled BUTTON_B falls back to Back. Moonlight has a scan-code remap for this layout (isNonStandardDualShock4 in its ControllerHandler), but it did not take effect while the helper filtered keys, consistent with what GamepadPassthrough (#1118) describes: re-injected keys no longer read as the controller's own, so Moonlight could neither remap them nor tell the two controllers apart.

gamepad_passthrough fixed both: Cross acts as A and the second controller joins as player 2. Set it in config.xml and rebuild, or "gamepad_passthrough": true in a theme's config.json. The trade is the one in the key-repeat section above: the remote buttons the helper answers (gear, app buttons) do nothing while a controller is connected, and come back when it disconnects. To check what a controller reports:

tools/tv.py 'dumpsys input' | grep -E 'Controller|KeyLayoutFile|Sources'
tools/tv.py 'getevent -lp'

One app refuses to play while the rest of the TV is online

An ad-blocking Private DNS is a common addition and the resolver answering is not proof that playback is unaffected. A player also calls measurement and analytics domains, and a block there comes back as the app's own error while the TV stays online. On a Master-G MGA3200 (ATV R1, R1, SCBC, Realtek, Android TV 9, PTT1.201001.001) with dns.adguard-dns.com in hostname mode, Max (com.wbd.stream) logged ERR_CONNECTION_REFUSED for its Braze and Conviva endpoints and stalled, while every other app played and Android's own connectivity check passed (#1060). Blocking pixel.spotify.com takes Spotify off the Freebox Player POP (examples/freebox-player-pop) the same way. So when one app fails and the rest play, set the mode back before debugging the app, then try the same title once more - on the MGA3200 that cleared the errors:

tools/tv.py 'settings put global private_dns_mode opportunistic'

A block can also leave playback alone and take out one feature. On a Chromecast with Google TV 4K (sabrina, Google TV 14, UTTC.250917.004) with dns.adguard-dns.com in hostname mode, Pluto TV (tv.pluto.android) kept playing but stopped remembering where an on-demand episode was left, from the day the resolver was set. AdGuard DNS answers 0.0.0.0 for sp.pluto.tv while the app's other hosts (boot, api, the catalogue and the stitcher) resolve. To check a host an app uses, ask the resolver and an unfiltered one from a computer:

nslookup sp.pluto.tv 94.140.14.14   # AdGuard DNS: 0.0.0.0
nslookup sp.pluto.tv 1.1.1.1        # a real address

An unfiltered resolver in Private DNS (dns.google), or an allow rule for that host in a personal AdGuard DNS profile, lets the app reach it again.

The other failure mode reads differently: when the TV itself says "No internet", either the resolver is down or unreachable, or — on a streaming box with no battery-backed clock after a mains power loss (or with auto_time off) — the clock booted at the firmware build date (Sat Aug 17 2024 on a Xiaomi TV Box S 2nd Gen, MiTV-AFKR0, jaws, Google TV 11, RTT0.211222.001.772), so DNS-over-TLS rejects the resolver's certificate as not yet valid while NTP cannot resolve its time server to fix the clock. Switching back to opportunistic and ensuring auto_time is 1 breaks the deadlock (settings put global auto_time 1 after the command above). What the optional resolver does, and what it costs, is in the README's "Can it block ads inside apps too?".

When you report how an app does on your TV, say how far it got, because each step can fail on its own: installed, opened, usable with the physical remote, signed in, and played a title with sound. Reaching the login screen is not playback. Quote the app's full error code (Netflix's UI-800-3, for example) rather than "doesn't work", since that is what the app's own help pages look up (#1008).

Xiaomi CN firmware: Settings freezes or crashes after the debloat

On China-market Xiaomi firmware the settings app is Xiaomi's own (com.xiaomi.mitv.settings), and it reads the TV's hardware configuration from the TvService system service that mitv.service registers. Disable that package and Settings breaks while everything else on the TV keeps working: opening it freezes for 20-30 s and drops back to Home (an ANR, not a reboot), and pressing Down twice from the first page, Network -> Display -> Sound, crashes it outright. Seen on a MiTV-ASTP0 (mulan, Android 9, SDK 28, Amlogic/droidlogic, 1.5 GB RAM; the version string says HyperOS 2, the base is still Android 9) in issue #1310, whose report could not quote ro.build.display.id: that firmware closes network ADB on every reboot.

The crash log names the cause:

tools/tv.py "logcat -b events -d | grep am_crash"
# java.lang.NumberFormatException, For input string: "NULL"   <- SoundView.init, Integer.valueOf

SoundView.init parses a value it got from mitv.common.ConfigurationManager with no guard, and with the service gone that manager retries ten times half a second apart, gives up and hands back the literal string NULL:

ConfigurationManager: getService() 0..9, service = null
ConfigurationManagerDefaultImpl: service null return

So it is neither the Settings app nor the debloat in general. mitv.service is /system/app/TvService, a PERSISTENT app whose TvApplication.onCreate starts TvServer, the thing that registers the service:

tools/tv.py "service list | grep -i tvservice"   # TvService: [mitv.internal.ITvService]

Keep the package. Every settings page that reads a hardware value then stalls about 5 s, and the one page that parses the answer crashes. The China-market lists here already say not to touch it; this is why.

The price is not the same on every set, and it is not a China-firmware problem only. On a MiTV-MOOQ1 (tarzan, Android TV 10, QTG3.200305.006.5283, the model in examples/xiaomi-mi-tv-4a, whose own list keeps the package) with mitv.service disabled, the brightness and contrast menu took about 30 s to open, and about 40 s after a power cycle at the wall; re-enabling the package and power-cycling again brought the menu back to normal, and the owner repeated both directions (issue #3085, from a set that had applied the second set of examples/xiaomi-mitv-aesp0). So on that firmware the picture menu alone costs 30-40 s to open, against the ~5 s this section measured on a CN set.

Re-enabling the package on its own does not bring Settings back. The service is registered at boot and the disabled package's PERSISTENT process starts anyway, so nothing looks different after pm enable and it is easy to conclude the debloat was innocent - the reporter lost a day to exactly that. Start the service by hand instead:

tools/tv.py "pm enable mitv.service"
tools/tv.py "am startservice -n mitv.service/.TvServer"
tools/tv.py "service list | grep -i tvservice"      # expect: TvService: [mitv.internal.ITvService]

A restart registers it too, and on that firmware the restart also turns network ADB off while the switch that turns it back on sits inside the Settings app that is broken: have ADB open before the reboot, or count on reaching Settings by hand. examples/xiaomi-mitv-aesp0 is the one list here that disables the package (its second set, in daily use since 21 Oct 2026); carrying that line to a set whose settings app is Xiaomi's takes Settings with it.

Xiaomi HyperOS 3: the power key does nothing, and music stops when the screen goes off

Two faults with one cause on a Xiaomi set running HyperOS 3: a short press of the remote's power button is not the framework's, and a screen that goes off force-stops every app the owner installed two seconds later. Both were read out of services.jar (baksmali) and logcat on a MiTV-MFFU0 (Xiaomi TV S Pro 100, finch, Android 14 / HyperOS 3, OS3.0.115.0.UFFMATV, MediaTek mt5877), and reported with the logcat lines in issue #2869.

The power key emits a plain KEYCODE_POWER and the framework's short-press policy is SHORT_PRESS_POWER_GO_XIAOAI_MODE: KEYCODE_SLEEP has no effect on it and mWakefulness stays awake. What turns the screen off is the stock launcher (PatchWall, com.mitv.tvhome) together with the two screensaver packages, com.xiaomi.mitv.aod.screensaver and com.xiaomi.mitv.hyper.screensaver, which render the off screen. Disable PatchWall - which the launcher install in examples/xiaomi-mitv-mffu0 needs - and the power button no longer turns the TV off; disable either screensaver package and nothing renders the off screen. The same build also needs aod_current_pattern_id at 107 (music CD) or 108 (music tape): at 110 (lyrics) the receiver logs its way to "nothing to do" and the panel never turns off. mitv.short.power.action (Settings.System) chooses 0 screen off (the default, instant wake), 1 standby (deep, and it cuts audio) or 2 shutdown, so raising it to 1 to "fix" a screen that will not go off turns screen-off into a set that looks powered off.

Screen off also takes the owner's apps with it, unless Xiaomi's cloud rule file says otherwise:

tools/tv.py 'logcat -d | grep killAllThirdApp'
# MitvActivityManagerService: PowerManagerService killAllThirdApp forcestop packageName:com.lxmusic.tv

MitvPowerManagerService.gotoSleepStayAliveProcess runs from the screen-off broadcast and asks MitvConfigParser for bit 0x8000 in /data/system/mitv_security_center.xml: a foreground app that file names is kept, anything else gets the launcher brought to the front and a thread that sleeps two seconds and calls killAllThirdApp. The file is pushed by cloud control and cannot be written from the shell, and the apps it names are the stock ones (QQ Music TV among them), so a third-party player loses its audio about two seconds after the screen goes off.

The firmware carries an off switch for that path: with Settings.Global disable_back_home_whitelist set to disable, the sleep routine returns before any of it runs.

tools/tv.py 'settings put global disable_back_home_whitelist disable'
tools/tv.py 'logcat -d | grep gotoSleepStayAliveProcess'   # expect: ... feature disable

It is a stock settings key, so it survives a reboot, and settings delete global disable_back_home_whitelist puts the behaviour back. The system then stops forcing the launcher to the front on screen-off too, which a third-party home screen wants anyway and a set still on its stock home screen would notice.

Both faults are this one build: another Xiaomi firmware may handle screen-off somewhere else and answer neither, and no second unit has been reported.

Settings freezes, then closes, in Accessibility

The highlight passes over Accessibility > Text-to-speech output and Settings stops responding; a few seconds later Android closes it ("Input dispatching timed out"). TV Settings previews the focused row, and that row's page starts Google's speech engine (com.google.android.tts, "Speech Recognition & Synthesis" in the Play Store) to name the current voice. On a Hisense 75U75N (hengshan, Google TV 12, STT2.230310.002, 23 and 24 October 2026) the engine took more than ten seconds to start, and leaving the row before it answered left Settings waiting for it. The debloat does not touch the engine. The ANR report names the wait:

tools/tv.py 'dumpsys dropbox --print system_app_anr' | grep -m2 TextToSpeech
#   at android.speech.tts.TextToSpeech.shutdown(TextToSpeech.java:914)
#   at com.android.tv.settings.system.TextToSpeechFragment.onDestroy(TextToSpeechFragment.java:218)

Clearing the engine's data fixed it on that set; the engine downloads its voice again the first time it is used:

tools/tv.py 'pm clear com.google.android.tts'

The Play Store had no newer engine there. Without the reset, wait on the row until the voice shows before moving on.

The router's domain rules stop applying to the TV after Private DNS

Private DNS in hostname mode sends every lookup over DNS-over-TLS straight to the resolver, past the router's own DNS. A router that filters or routes traffic by domain through its resolver - podkop and its forks on OpenWrt, mihomo/Clash in fake-IP mode, a VPN subscription with domain lists, Pi-hole or AdGuard Home - then never sees the TV's lookups, and its rules stop applying to the TV without any error. On a TCL Smart TV Pro (G05, Android 11, AR2101/AR28) behind such a router, with dns.adguard-dns.com in hostname mode, the TV got real addresses instead of the router's fake-IP range (198.18.0.0/15), so traffic the router would have sent through its tunnel went out directly: services that only work through that route broke or slowed down, and the resolver itself answered 127.0.0.1 for api.themoviedb.org (#1385).

To tell, compare the address the TV gets with the router's answer for the same host:

tools/tv.py 'ping -c1 -W1 <host>'     # the first line shows the address the TV resolved
nslookup <host> <router ip>           # on the computer: what the router answers

A fake-IP or other router-made answer next to a real address on the TV means Private DNS is going around the router. Then either set the mode back, so the TV asks the router again, and block ads on the router instead (most of these setups can):

tools/tv.py 'settings put global private_dns_mode opportunistic'

or keep Private DNS and accept that the router's domain rules no longer cover the TV. Write down which one you chose, so the next "this one service is slow on the TV" does not start from scratch.

A VPN app on the TV makes strict Private DNS fail every lookup

Hostname mode applies the specifier to the VPN network as well as to Wi-Fi, and with a VPN app connected on the TV itself it can fail validation there while Wi-Fi still reports it valid. Seen on a Xiaomi Mi Box S (MIBOX4, oneday, Android TV 9, PI.3933) with the Tailscale app connected for all apps: setting dns.adguard-dns.com in hostname mode broke every name lookup within seconds. ping youtube.com and ping dns.adguard-dns.com both answered unknown host, ping 1.1.1.1 still worked, and dumpsys connectivity showed the Wi-Fi network VALIDATED with UsePrivateDns: true, the VPN network with DnsAddresses: [100.100.100.100, ...], UsePrivateDns: true and lastValidated{false} (#3130). The lookups go to the VPN's own resolver, and DNS-over-TLS to that specifier through it did not validate. ADB kept working throughout, since it is an IP connection and needs no lookup, so the undo below can be sent over the same session.

Check for a running VPN before switching Private DNS on:

tools/tv.py 'dumpsys connectivity' | grep 'type: VPN'

A NetworkAgentInfo line there means one is up: leave Private DNS off on that set, and filter ads on the router or in the VPN's own DNS settings instead. If it is already on, this puts lookups back within a few seconds:

tools/tv.py 'settings put global private_dns_mode opportunistic'
tools/tv.py 'settings delete global private_dns_specifier'

Not checked in that report: other VPN clients and Android versions, and whether a specifier the VPN's resolver can reach validates.

After a power cut the clock is wrong and no app connects, with strict Private DNS

A TV that keeps no time without power can start with an old date, and in hostname mode that is enough to take it off the internet for good. Seen on a Philips 70PUS8545/12 (TPM191E, PH9M_EA_5599, MediaTek mt5887, Android TV 12, STT2.230526.001) with dns.adguard-dns.com in hostname mode: after a power cut it came up on 9 December 2025, almost a year back. The resolver's certificate does not validate against that date (validateDnsTlsServer returned 0 in logcat), and hostname mode does not fall back, so no name resolved at all. The TV's own time service (org.droidtv.icp on this set) asks pool.ntp.org, a name, so it could never correct the clock, and Android's auto_time is 0 from the factory on this firmware, so nothing else set it either. Every app failed, and nothing on screen pointed at DNS. ADB kept working, since it needs no lookup.

The two settings to read first:

tools/tv.py 'date; settings get global private_dns_mode'

A date in the past next to hostname is this loop. Set the mode back, then restart the TV from its menu, so the time service can reach its server again:

tools/tv.py 'settings put global private_dns_mode opportunistic'

On that set the first restart put the clock right; Android's own internet check, which had failed on the old date, passed only after a second one. Hostname mode was left off there afterwards, since the next power cut would start the loop again, and ad blocking on the router is not affected by the TV's clock. Not checked: other brands' time services, and whether a TV whose auto_time is on gets out of the loop by itself.

An app that runs a local VPN is on until the next boot, then gone

A DNS filter, an ad blocker or a VPN client you keep, set to start with the TV, works once switched on, and after every reboot the TV comes up without it: no tun0, no process. Seen with the AdGuard app (4.6.207, local VPN mode) on an Anker Nebula D2426 projector (Android TV 14, UKNV.260603.001, 1.7 GB), and the mechanism is Android's, not the app's. Its boot receiver did run: the TV's log shows ActivityManager: Start proc <pid>:<package> ... for broadcast {.../BootUpReceiver}, then 12 s later Process <package> (pid <pid>) has died: cch+95 CEM, while the app's own lines still say it is loading. The receiver had returned and handed the loading to a thread, no foreground service was up yet, so to Android the process was an empty cached one, and this firmware keeps very few of those: tools/tv.py 'dumpsys activity settings' | grep CUR_MAX prints CUR_MAX_CACHED_PROCESSES=8 and CUR_MAX_EMPTY_PROCESSES=4. The first boot services push it out before it reaches the VPN. Any such app that loads slowly on a small TV can hit this; Private DNS (the README's option) is not affected by that eviction, since the system resolver does the work - but while the app is up, the specifier applies to its VPN network as well, which is the failure above.

What does not help: the background exemptions (cmd appops set <package> RUN_ANY_IN_BACKGROUND allow, dumpsys deviceidle whitelist +<package>, am set-standby-bucket <package> active), because the app was never restricted, only evicted; and device_config put activity_manager max_cached_processes 32, which leaves CUR_MAX_CACHED_PROCESSES at the maker's 8. What does: make the system itself bring the VPN up at boot, by declaring the app as the always-on VPN. The app must have had its VPN consent dialog accepted once before (it has, if its protection was ever switched on by hand):

tools/tv.py 'settings put secure always_on_vpn_app <package>; settings put secure always_on_vpn_lockdown 0'

At the next boot the log shows Background started FGS: Allowed [callingPackage: <package> about 25 s after the process start, and tools/tv.py 'ip addr show tun0' has an address, before the firmware's own boot-time calls go out (about two minutes in, here). Keep always_on_vpn_lockdown at 0: with 1 Android drops every packet until the VPN is up, and a TV whose app fails to start stays offline, with no screen that says why. Undo: tools/tv.py 'settings delete secure always_on_vpn_app; settings delete secure always_on_vpn_lockdown'.

The home screen reads about 40 ms and every tuning change lands inside the noise

Seen on: the Wanbo X5 Pro's built-in stick (4K Google TV Stick, Skyworth HP4609-Skyworth, Amlogic, Google TV 14, UKG3.260128.001.C3.3.5_20260723), 1920×1080 at 60 Hz, with a third-party launcher as Home.

dumpsys gfxinfo reported a 50th-percentile frame of 42 ms and roughly 14% janky frames, and it stayed within a couple of milliseconds across changes that should each have helped: a release build with R8, a decode sampled to the panel size, hardware bitmaps, a pre-rendered glow and a hardware layer on the backdrop. Read the phases before changing anything.

The app is probably idle and innocent. Two numbers settle it:

$ adb shell dumpsys gfxinfo <pkg> reset; sleep 3; adb shell dumpsys gfxinfo <pkg> | grep "Total frames"
Total frames rendered: 0

and in dumpsys gfxinfo <pkg> framestats, the UI thread span (DrawStart - PerformTraversalsStart) was 0.1 ms. If the app draws nothing when idle and its UI thread is near zero, the remaining time is the render and present path, and no Java change reaches it.

Frames slip in whole vsync intervals. At 60 Hz a frame that misses its deadline does not land at 17 ms, it lands at 33 ms. A p50 around 40 ms means "usually two ticks, sometimes three", not "slow by 40 ms". Shaving a third off anything does nothing; the frame has to cross back under 16.7 ms.

Check whether the device is the ceiling before touching the app:

$ adb shell dumpsys SurfaceFlinger | grep -E "HWC missed|GPU missed"
HWC missed frame count: 3494
GPU missed frame count: 9

HWC missed counts frames the hardware composer failed to present. When it dwarfs GPU missed, the display pipeline is dropping the frames, and app-side work is spent. Confirm the panel really is 60 Hz first, because a 24 Hz mode with a 42 ms frame looks identical to jank:

$ adb shell dumpsys display | grep mUserPreferredMode
 mUserPreferredMode={id=-1, width=1920, height=1080, fps=60.000004, ...}

If GPU missed is the larger number, the cost is pixels, not code. A smaller source texture does not help: the budget is destination pixels, so 4K artwork decoded to 720p still writes 1920×1080. Count the full-screen layers instead, and look for two of them stacked — a wallpaper plus a scrim, or two bitmaps crossfading — because pre-compositing them into one bitmap on a worker thread is the only change of that kind that moved anything here.

The one lever that moved it was the Skia backend:

$ adb shell setprop debug.hwui.renderer skiavk
$ adb shell am force-stop <pkg>      # then press Home

42 ms → 34 ms p50 and 13.7% → 10.5% janky on this stick. It is a debug property: it resets on every reboot, so it is something to re-apply, not to ship. Confirm with dumpsys gfxinfo <pkg> | grep Pipeline (Pipeline=Skia (Vulkan)); revert with setprop debug.hwui.renderer opengl.

Three changes that did nothing here, so they are not worth an evening: decoding the backdrop at 720p, a hardware layer on a view that invalidates every frame (the layer is re-recorded each frame and costs more), and shortening a 1.5 s backdrop crossfade to 0.6 s.