Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions artemis/runtime/helper_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,23 @@

PACKAGE_NAME = "com.artemis.helper"
SERVICE_NAME = f"{PACKAGE_NAME}/.ArtemisAccessibilityService"


def _is_helper_service(entry: str) -> bool:
"""Whether one ``enabled_accessibility_services`` entry names the helper service.

The same component has two spellings: adb writes the short form
(``com.artemis.helper/.ArtemisAccessibilityService``) while the Settings UI
writes the fully-qualified one (``com.artemis.helper/com.artemis.helper.Artemis…``).
ROMs that reject the adb write (e.g. ColorOS/realme) leave only the
Settings-UI spelling, so compare the normalized component, not the string.
"""
pkg, _, cls = entry.strip().partition("/")
if cls.startswith("."):
cls = pkg + cls
return pkg == PACKAGE_NAME and cls == f"{PACKAGE_NAME}.ArtemisAccessibilityService"


TOKEN_RECEIVER = f"{PACKAGE_NAME}/.TokenReceiver"
TOKEN_ACTION = f"{PACKAGE_NAME}.SET_TOKEN"
#: Loopback port the service binds on the device. Never used as a host port.
Expand Down Expand Up @@ -408,7 +425,11 @@ def is_service_enabled(self, serial: str) -> bool:
serial, "shell", "settings", "get", "secure", "enabled_accessibility_services"
)
enabled = (result.stdout or "").strip()
return SERVICE_NAME in enabled.split(":") if enabled and enabled != "null" else False
return (
any(_is_helper_service(s) for s in enabled.split(":"))
if enabled and enabled != "null"
else False
)

def existing_forward(self, serial: str) -> int | None:
"""Host port of an adb forward that already targets the helper on ``serial``."""
Expand Down Expand Up @@ -602,7 +623,7 @@ def _enable_service(self, serial: str) -> bool:
)
current = (result.stdout or "").strip()
services = [s for s in current.split(":") if s and s != "null"] if current else []
if SERVICE_NAME in services:
if any(_is_helper_service(s) for s in services):
# Already enabled (the common, up-to-date case): no settle wait.
self._adb(
serial, "shell", "settings", "put", "secure", "accessibility_enabled", "1"
Expand Down Expand Up @@ -640,7 +661,7 @@ def _revive_service(self, serial: str) -> None:
serial, "shell", "settings", "get", "secure", "enabled_accessibility_services"
)
current = (result.stdout or "").strip()
others = [s for s in current.split(":") if s and s != "null" and s != SERVICE_NAME]
others = [s for s in current.split(":") if s and s != "null" and not _is_helper_service(s)]
self._adb(
serial,
"shell",
Expand Down Expand Up @@ -793,7 +814,9 @@ def uninstall(self, serial: str) -> bool:
serial, "shell", "settings", "get", "secure", "enabled_accessibility_services"
)
current = (result.stdout or "").strip()
services = [s for s in current.split(":") if s and s != "null" and s != SERVICE_NAME]
services = [
s for s in current.split(":") if s and s != "null" and not _is_helper_service(s)
]
self._adb(
serial,
"shell",
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/runtime/test_helper_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,34 @@ def test_uninstall_disables_service_and_removes_package(env):
assert adb.forwards == []


# The Settings UI stores the fully-qualified component; adb stores the short form.
# ROMs that reject the adb secure-settings write (ColorOS/realme) only ever have the former.
QUALIFIED_SERVICE_NAME = "com.artemis.helper/com.artemis.helper.ArtemisAccessibilityService"


def test_service_enabled_through_settings_ui_is_recognised(env):
adb, _, manager = env
adb.installed[SERIAL] = 2
adb.enabled_services[SERIAL] = f"com.android.talkback/.TalkBackService:{QUALIFIED_SERVICE_NAME}"

assert manager.is_service_enabled(SERIAL)
result = manager.provision(SERIAL)
assert result.ok
puts = _calls(
adb, ["-s", SERIAL, "shell", "settings", "put", "secure", "enabled_accessibility_services"]
)
assert puts == [] # already enabled: the setting is left exactly as the user wrote it


def test_uninstall_removes_service_enabled_through_settings_ui(env):
adb, _, manager = env
adb.installed[SERIAL] = 2
adb.enabled_services[SERIAL] = f"com.android.talkback/.TalkBackService:{QUALIFIED_SERVICE_NAME}"

assert manager.uninstall(SERIAL)
assert adb.enabled_services[SERIAL] == "com.android.talkback/.TalkBackService"


def test_transport_id_parsing_handles_missing_device():
adb = FakeAdb()
manager = AccessibilityHelperManager(run_adb=adb, ping=lambda _p: None, bundled=None)
Expand Down
Loading