From e4b1ffd7418ec342ed3a2344c85d12b66af4671c Mon Sep 17 00:00:00 2001 From: Mazin Kanuga <97324132+TechnoDudeX@users.noreply.github.com> Date: Sun, 25 Oct 2026 09:25:41 -0400 Subject: [PATCH] docs: a Haier H50H5F restart loop after a debloat, and the factory reset as the way back (#1578) docs/TROUBLESHOOTING.md gets a bullet for the Haier H50H5F (Google TV 14) that restarted in a loop after a debloat: what the agent disabled is not known, because the disabled list attached to the report matches the backup PROMPT.md takes before any change and five of the eight packages the reply names from it are disabled from the factory on the Haier-built AQUA MatrixTV EE 50, and the way back is the factory reset in the recovery menu. The README's "Is this safe?" answer now says that reset puts back everything the tools change, and tests/test_haier_h50h5f_loop_note.py pins both. Not tried on a TV. Refs #1578, refs #1592. Co-Authored-By: Claude Opus 5.5 --- README.md | 7 ++ docs/TROUBLESHOOTING.md | 15 ++++ tests/test_haier_h50h5f_loop_note.py | 116 +++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 tests/test_haier_h50h5f_loop_note.py diff --git a/README.md b/README.md index 71f4256a..5a23045e 100644 --- a/README.md +++ b/README.md @@ -809,6 +809,13 @@ in the user profile. The risk is disabling something the TV leans on, which is w applied in small batches with a restore script ready. The TV's own Settings → Apps page can re-enable packages even without the tools. +If the TV no longer starts far enough for that, or for ADB, a factory reset from its recovery menu is +usually the way back ([#1578](https://github.com/Gathin23/cleanyourtv/issues/1578)): everything these +tools change lives in the user data the reset wipes, so every disabled app comes back and the new +launcher is gone, along with your own apps and accounts. A TV that still restarts in a loop after the +reset points to the hardware rather than the debloat ([#1592](https://github.com/Gathin23/cleanyourtv/issues/1592)); +[Troubleshooting](docs/TROUBLESHOOTING.md#working-on-the-tv) has the cases seen so far. +
diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 5501e41e..d238f4f0 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -267,6 +267,21 @@ Things that bit us or contributors, so they don't bite you. Commands use `tools/ `com.android.settings`, `com.android.tv.settings` or `android`), with no switch to override it. 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](https://github.com/Gathin23/cleanyourtv/issues/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](../examples/aqua-matrixtv-ee-50/debloat.txt) 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). - **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](https://github.com/Gathin23/cleanyourtv/issues/795): Realtek `rtd298x`, Android 4.4.2, SDK 19). 17 packages were disabled over Ethernet ADB, each diff --git a/tests/test_haier_h50h5f_loop_note.py b/tests/test_haier_h50h5f_loop_note.py new file mode 100644 index 00000000..384d2087 --- /dev/null +++ b/tests/test_haier_h50h5f_loop_note.py @@ -0,0 +1,116 @@ +"""The Haier H50H5F restart-loop bullet (#1578) must not present the attached list as the agent's work. + +The report attached the six files PROMPT.md has the agent save before changing anything, and the reply +on the issue named eight packages from its disabled list. 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. The +bullet says so, keeps the way back that holds either way (the factory reset in the recovery menu), and +does not tell the reader to turn the eight on as an undo. + +Run from the repository root: python3 -m unittest discover -s tests -v +""" +from pathlib import Path +import re +import unittest + + +ROOT = Path(__file__).resolve().parents[1] +TROUBLE = ROOT / "docs" / "TROUBLESHOOTING.md" +README = ROOT / "README.md" +AQUA = ROOT / "examples" / "aqua-matrixtv-ee-50" / "debloat.txt" +TITLE = "- **The TV restarts in a loop after a debloat**" + +# The eight packages the reply on #1578 read off the attached disabled-package list. +EIGHT = [ + "com.cltv.mal", + "com.cltv.hybrid", + "com.mediatek.dtv.tvinput.isdbtuner", + "com.mediatek.dtv.tvinput.atsctuner", + "com.haier.gamingbar", + "ru.haier.evo", + "com.haier.apps.evoSmart", + "it.hon.onboarding", +] + + +def bullet(text, start): + """The markdown bullet that begins with `start`, up to the next top-level bullet, on one line.""" + lines = text.split("\n") + first = next(i for i, line in enumerate(lines) if line.startswith(start)) + out = [lines[first]] + for line in lines[first + 1:]: + if line.startswith("- **") or line.startswith("## "): + break + out.append(line) + return re.sub(r"\s+", " ", "\n".join(out)) + + +def factory_disabled(path): + """Package names the list header records after "Already disabled from the factory:".""" + header = " ".join(line.lstrip("#").strip() for line in path.read_text(encoding="utf-8").splitlines() + if line.startswith("#")) + after = header.split("Already disabled from the factory:", 1)[1].split(". ", 1)[0] + return set(re.findall(r"[a-z][\w]*(?:\.[\w]+)+", after)) + + +class HaierLoopBulletTest(unittest.TestCase): + def setUp(self): + self.text = TROUBLE.read_text(encoding="utf-8") + self.loop = bullet(self.text, TITLE) + + def test_bullet_sits_under_working_on_the_tv(self): + section = self.text.split("## Working on the TV", 1)[1].split("\n## ", 1)[0] + self.assertIn(TITLE, section) + + def test_bullet_names_the_set_and_the_report(self): + for token in ("Haier H50H5F", "Google TV 14", "seen once", "issues/1578"): + with self.subTest(token=token): + self.assertIn(token, self.loop) + + def test_bullet_does_not_say_the_agent_disabled_the_eight(self): + self.assertIn("What the agent disabled is not known", self.loop) + self.assertNotRegex(self.loop, r"(?i)the agent's list disabled") + self.assertNotRegex(self.loop, r"(?i)\bboth tuner inputs\b") + self.assertNotRegex(self.loop, r"(?i)likely cause") + self.assertNotIn("`com.cltv.mal`**", self.loop, "the bold symptom must not name a cause") + + def test_bullet_says_the_list_may_be_the_backup(self): + self.assertIn("PROMPT.md", self.loop) + self.assertIn("before changing anything", self.loop) + self.assertIn("may be the TV's state before the debloat", self.loop) + + def test_five_of_the_eight_are_factory_disabled_on_the_aqua(self): + # The count in the bullet must match the AQUA list it links. + on_aqua = factory_disabled(AQUA) & set(EIGHT) + self.assertEqual(len(on_aqua), 5, sorted(on_aqua)) + self.assertIn("five of those eight are disabled from the factory", self.loop) + self.assertIn("(../examples/aqua-matrixtv-ee-50/debloat.txt)", self.loop) + self.assertTrue(AQUA.is_file()) + + def test_bullet_does_not_offer_the_eight_as_the_undo(self): + self.assertNotRegex(self.loop, r"(?i)lists all eight|re-enable all eight") + for package in EIGHT[2:]: + with self.subTest(package=package): + self.assertNotIn(f"pm enable {package}", self.loop) + self.assertIn("would switch on what the factory left off", self.loop) + self.assertIn("each package from the list that was applied", self.loop) + + def test_bullet_keeps_the_way_back(self): + self.assertIn("adb mdns services", self.loop) + self.assertIn("recovery menu", self.loop) + self.assertIn("factory reset turns every disabled package back on", self.loop) + self.assertIn("nothing was uninstalled", self.loop) + + +class ReadmeSafetyAnswerTest(unittest.TestCase): + def test_readme_points_to_the_factory_reset_and_the_bullet(self): + text = README.read_text(encoding="utf-8") + answer = text.split("Is this safe? Can it brick the TV?", 1)[1].split("
", 1)[0] + answer = re.sub(r"\s+", " ", answer) + for token in ("factory reset from its recovery menu", "issues/1578", "issues/1592", + "docs/TROUBLESHOOTING.md#working-on-the-tv"): + with self.subTest(token=token): + self.assertIn(token, answer) + + +if __name__ == "__main__": + unittest.main()