doctor: use NameHasOwner for xdg-desktop-portal presence check
check_portal() previously introspected the portal object path directly,
which dbus-next refuses to parse on healthy GNOME sessions where the
portal exposes hyphenated property names such as power-saver-enabled.
The bare `except Exception` masked the real InvalidMemberNameError and
caused `solstone-linux doctor` to report `fail`, blocking
`make install-service` on working systems.
Fix: ask the D-Bus daemon (org.freedesktop.DBus) whether
org.freedesktop.portal.Desktop is a currently-owned well-known name via
NameHasOwner. The daemon's introspection XML has no hyphenated members,
so the parser bug does not apply. The check is bounded by a 2s
asyncio.wait_for and distinguishes three failure modes (not registered,
bus unreachable, timed out) with dedicated detail strings.
Adds five direct unit tests for check_portal, including a regression
pin that fails against the old implementation when a fake bus would
raise InvalidMemberNameError on portal-path introspection.
Follow-up (not in scope): activity.py and screencast.py also call
bus.introspect() on service object paths and would hit the same
parser bug on any service that exposes a hyphenated member; today
they hide it behind `except Exception: pass` and degrade silently.
Co-Authored-By: OpenAI Codex <codex@openai.com>