mirror of
https://github.com/Gathin23/cleanyourtv.git
synced 2026-10-27 19:54:45 +00:00
docs: the Google TV Streamer reproduces the setupwraith casting and crash-loop notes (#500)
docs/TROUBLESHOOTING.md: the casting break was scoped to Google's own Chromecast hardware; a kirkwood box (UTTK.260317.003) hits it with the same log lines, and there the credentials live only in the receiver's process, so am force-stop loses them just as a reboot does. The crash-loop bullet gets that box's trace (positionChildAt / Task.moveToBack / TransitionController, every ~47 s, DeadSystemException) and the HOME bullet gets the measurement that the resolver table and the HOME role holder can both name the new launcher while the key still opens setupwraith/.RecoveryActivity. PROMPT.md says to confirm with 'dumpsys activity activities | grep mFocusedApp' before claiming the takeover worked; README's setupwraith bullet no longer implies only Chromecast loses casting. No examples/ change: #501 brings the kirkwood list, and its header already carries these quirks.
This commit is contained in:
1 parent
687b5ee2a3
commit
c66cfadad5
4 files changed
+144
-7
No files matched your search
@@ -74,7 +74,11 @@ Launcher
|
||||
Android TV launcher instead (Java, AndroidX RecyclerView, minSdk matching the TV, debug build is fine),
|
||||
install it over ADB, set it as the default home and disable the stock launcher. If several apps claim
|
||||
the HOME intent, list them with `cmd package query-activities -a android.intent.action.MAIN -c
|
||||
android.intent.category.HOME` and set the default with `cmd package set-home-activity`.
|
||||
android.intent.category.HOME` and set the default with `cmd package set-home-activity`. Those two
|
||||
commands can both name your launcher while Home still opens something else: on some Google TV builds
|
||||
`set-home-activity` prints `Success` and changes nothing, and setupwraith's `.RecoveryActivity`
|
||||
keeps the key. Confirm with `dumpsys activity activities | grep mFocusedApp` before telling me the
|
||||
takeover worked.
|
||||
- Before disabling the stock launcher, check which other apps claim HOME: on Google TV the setup wizard
|
||||
(`com.google.android.tungsten.setupwraith`) takes over even after the stock launcher is disabled, and
|
||||
the matching `examples/` header says what to do. Before disabling setupwraith, run
|
||||
|
||||
@@ -332,7 +332,7 @@ three that matter most:
|
||||
|
||||
- **macOS blocks `adb connect`** ("No route to host") under Local Network privacy. `tools/tv.py` avoids the binary.
|
||||
- **Disable the stock launcher only after the new one is installed and running**, or Home lands on a factory test app. The install script orders this for you.
|
||||
- **On Google TV the setup wizard** (`com.google.android.tungsten.setupwraith`) can take Home and crash-loop the system after the stock launcher is disabled. Disable it too, and list it in your `debloat.txt` so restore brings it back. But check `dumpsys wifi | grep -o 'cname=[^ ]*'` first: if the Wi-Fi network was saved during setup it belongs to setupwraith, and disabling it deletes the network (re-join it in Settings first; `tools/disable.sh` skips setupwraith until you do). On Google's own Chromecast it also costs phone casting, see [Troubleshooting](docs/TROUBLESHOOTING.md). Stuck already? `tools/tv.py 'pm enable --user 0 com.google.android.apps.tv.launcherx'`.
|
||||
- **On Google TV the setup wizard** (`com.google.android.tungsten.setupwraith`) can take Home and crash-loop the system after the stock launcher is disabled. Disable it too, and list it in your `debloat.txt` so restore brings it back. But check `dumpsys wifi | grep -o 'cname=[^ ]*'` first: if the Wi-Fi network was saved during setup it belongs to setupwraith, and disabling it deletes the network (re-join it in Settings first; `tools/disable.sh` skips setupwraith until you do). On Google's own Chromecast and on the Google TV Streamer it also costs phone casting, see [Troubleshooting](docs/TROUBLESHOOTING.md). Stuck already? `tools/tv.py 'pm enable --user 0 com.google.android.apps.tv.launcherx'`.
|
||||
|
||||
## Questions people ask
|
||||
|
||||
|
||||
+22
-5
@@ -88,7 +88,14 @@ Things that bit us or contributors, so they don't bite you. Commands use `tools/
|
||||
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.
|
||||
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.
|
||||
- **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
|
||||
@@ -113,9 +120,14 @@ Things that bit us or contributors, so they don't bite you. Commands use `tools/
|
||||
`pm disable-user` on `setupwraith/.RecoveryActivity` is refused with `SecurityException: Shell
|
||||
cannot change component state`. So either debloat without setupwraith and launcherx, 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. Reported on Google's own
|
||||
Chromecast hardware so far; the Xiaomi Mi Box S 2nd gen list says casting from a Pixel still works
|
||||
there with setupwraith off.
|
||||
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. 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.
|
||||
- **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
|
||||
@@ -123,7 +135,12 @@ Things that bit us or contributors, so they don't bite you. Commands use `tools/
|
||||
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. If you're already in the loop:
|
||||
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
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
"""The setupwraith notes must carry the second box that reproduces them (#500).
|
||||
|
||||
docs/TROUBLESHOOTING.md already documented the casting break and the `.RecoveryActivity` crash loop
|
||||
from a Chromecast with Google TV HD and a Xiaomi MiTV-MZTU0. A Google TV Streamer (`kirkwood`,
|
||||
`UTTK.260317.003`) reproduces both and adds two facts the doc was missing: the cast credentials live
|
||||
only in the receiver's process, so a force-stop loses them just as a reboot does, and the HOME
|
||||
resolver / role holder can both name the new launcher while the key still opens setupwraith.
|
||||
|
||||
The tests read the bullets as bullets, not as grep hits: a sentence that drifts into a neighbouring
|
||||
bullet would leave the doc just as useless. 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]
|
||||
DOC = (ROOT / "docs" / "TROUBLESHOOTING.md").read_text(encoding="utf-8")
|
||||
README = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
PROMPT = (ROOT / "PROMPT.md").read_text(encoding="utf-8")
|
||||
KIRKWOOD = "UTTK.260317.003"
|
||||
|
||||
|
||||
def bullets(text):
|
||||
"""Split a markdown doc into its `- **Title**` bullets, wrapping lines included."""
|
||||
found, current = [], None
|
||||
for line in text.splitlines():
|
||||
if line.startswith("- **"):
|
||||
if current is not None:
|
||||
found.append(current)
|
||||
current = line
|
||||
elif current is not None and (line.startswith(" ") or not line.strip()):
|
||||
current += "\n" + line
|
||||
else:
|
||||
if current is not None:
|
||||
found.append(current)
|
||||
current = None
|
||||
if current is not None:
|
||||
found.append(current)
|
||||
return found
|
||||
|
||||
|
||||
def bullet_containing(needle):
|
||||
matches = [b for b in bullets(DOC) if needle in b]
|
||||
assert len(matches) == 1, f"expected one bullet with {needle!r}, found {len(matches)}"
|
||||
return re.sub(r"\s+", " ", matches[0])
|
||||
|
||||
|
||||
class KirkwoodNoteTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.casting = bullet_containing("Casting from a phone fails")
|
||||
self.crash = bullet_containing("crash-looped")
|
||||
self.home = bullet_containing("still doesn't own Home")
|
||||
|
||||
def test_the_casting_break_is_not_scoped_to_googles_own_hardware(self):
|
||||
self.assertIn("Google TV Streamer", self.casting)
|
||||
self.assertIn(KIRKWOOD, self.casting)
|
||||
self.assertNotIn("hardware so far", self.casting,
|
||||
"the 'so far' wording is what the report corrects")
|
||||
|
||||
def test_the_casting_bullet_names_where_the_provider_lives_on_kirkwood(self):
|
||||
# The reporter checked `dumpsys package providers`: there is no separate tvsetup package,
|
||||
# the CastInfoProvider classes ship inside the setupwraith APK. That is why disabling the
|
||||
# wizard takes casting with it on a box Google never shipped a wizard for.
|
||||
self.assertIn("CastInfoProvider", self.casting)
|
||||
self.assertIn("setupwraith", self.casting)
|
||||
|
||||
def test_a_receiver_restart_loses_the_credentials_too(self):
|
||||
self.assertIn("force-stop", self.casting)
|
||||
self.assertIn("mediashell", self.casting)
|
||||
self.assertIn("Retry loading factory credentials", self.casting)
|
||||
self.assertIn("next reboot", self.casting,
|
||||
"the reboot case is still the common one and must stay")
|
||||
|
||||
def test_the_casting_workaround_still_says_what_to_do(self):
|
||||
# The correction must not cost the instruction: enable, wait for the signer, disable again.
|
||||
self.assertIn("ClientAuthSigner", self.casting)
|
||||
self.assertIn("re-enable setupwraith after each boot", self.casting)
|
||||
self.assertIn("cannot change component state", self.casting)
|
||||
|
||||
def test_the_home_bullet_says_resolver_and_role_holder_can_both_lie(self):
|
||||
self.assertIn("mFocusedApp", self.home)
|
||||
self.assertIn("resolve-activity", self.home)
|
||||
self.assertIn("get-role-holders", self.home)
|
||||
self.assertIn("RecoveryActivity", self.home)
|
||||
|
||||
def test_the_home_bullet_keeps_the_package_level_way_out(self):
|
||||
# Flipping to the component would be wrong: the shell cannot change component state.
|
||||
self.assertIn("pm disable-user", self.home)
|
||||
self.assertIn("SecurityException", self.home)
|
||||
|
||||
def test_the_crash_loop_bullet_carries_the_kirkwood_trace(self):
|
||||
self.assertIn("positionChildAt", self.crash)
|
||||
self.assertIn("Task.moveToBack", self.crash)
|
||||
self.assertIn("DeadSystemException", self.crash)
|
||||
self.assertIn(KIRKWOOD, self.crash)
|
||||
|
||||
def test_the_crash_loop_bullet_keeps_the_way_out(self):
|
||||
self.assertIn("pm enable --user 0 com.google.android.apps.tv.launcherx", self.crash)
|
||||
self.assertIn("FATAL EXCEPTION IN SYSTEM PROCESS", self.crash)
|
||||
|
||||
def test_the_readme_clause_covers_both_boxes(self):
|
||||
clause = next(line for line in README.splitlines() if "costs phone casting" in line)
|
||||
self.assertIn("Google TV Streamer", clause)
|
||||
self.assertIn("Chromecast", clause)
|
||||
self.assertIn("docs/TROUBLESHOOTING.md", clause)
|
||||
|
||||
def test_the_prompt_asks_for_the_focused_app_before_claiming_success(self):
|
||||
self.assertIn("mFocusedApp", PROMPT)
|
||||
self.assertIn("set-home-activity", PROMPT)
|
||||
self.assertIn("query-activities", PROMPT)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user