Repository navigation
feat(linux): Linux desktop & CLI implementation (stacked on #23) - #12
jochenkirstaetter wants to merge 5 commits into
Conversation
|
Hi Jochen, great progress, and thanks for keeping it as a draft while it grows. I approved CI on this PR so you get real feedback as you go. A few things before this can land:
Mark it ready for review whenever the Linux app does the basics end to end (pairing, notifications, clipboard, files). |
30a776d to
68e5ad4
Compare
|
Hi @thtbee Everything well noted. I'm going to comply with the contributing constraints. A few notes on your remarks on how I'm going to proceed.
Ohh, it's ready since milestone 3 already. I'm currently working on milestone 5 - reverse mirroring, Phone-to-PC features, and so forth. Cheers, JoKi |
68e5ad4 to
14678c6
Compare
|
Hi @thtbee Seen the build errors and will address them. Cheers |
1914241 to
da34f29
Compare
|
Hello @thtbee I know it's a huge PR and the majority of changes are focusing on Linux and new tests. Following is a summary of the changes made to the non-Linux areas. Kindly have a look and give me any feedback that needs further review and improvement. The whole branch has already been rebased onto Summary of Changes to Shared & Non-Linux Areas1. Core Engine, CLI & FFI (core/)features.rs: identity.rs & Cargo.toml: deck.rs: node.rs & session.rs (Cross-platform E2E reliability fixes): clipboard.rs, handoff.rs, timeline.rs: lib.rs: main.rs & Cargo.toml: ────── 2. Android App (android/)PhoneClipboard.kt: ThemeAndLocalizationTest.kt: ────── 3. Shared Desktop Rust Modules (desktop/app/ & desktop/vcam/)Cargo.toml & build.rs: main.rs, core_host.rs, local_api.rs, icons.rs: Platform-Gated Desktop Subsystems (all existing #[cfg(windows)] code paths preserved intact, with #[cfg(target_os = "linux")] implementations added alongside): Desktop State, Settings & Bridge Improvements (settings.rs, prefs.rs, app.rs, timeline.rs, transfers.rs, Virtual Camera Shared Memory Crate (desktop/vcam/): ────── 4. Desktop C++ Windowing & QML UI (desktop/app/cpp/ & desktop/app/qml/)native_window.cpp & app_helpers.cpp: Cross-Platform QML Rendering & Font Compatibility (desktop/app/qml/): ────── 5. Build Scripts & Spikes (scripts/check.ps1, spikes/s1-qt-rust/)check.ps1: Updated the Android check step to invoke ./gradlew on Linux/macOS and .\gradlew.bat on Windows. spikes/s1-qt-rust/: Guarded the Windows D3D11 spike code with #[cfg(windows)] / #ifdef _WIN32 so cargo test |
|
Hi @jochenkirstaetter, thank you for the summary of the shared-code changes. It makes this much easier to review. A heads-up before you do more:
Please rebase onto the current |
thtbee
left a comment
There was a problem hiding this comment.
Hi @jochenkirstaetter, thank you for this, and for the summary of the shared-code changes. Getting NectarLink onto Linux is something a lot of people have asked for, and the amount of work here shows. This review covers what you asked about: the changes outside the Linux-only code (core, FFI, Android, and the desktop code that also runs on Windows). The Linux-only modules get their own pass once the branch is split (see the end).
Must change before merge
-
The Linux key isn't actually protected (
core/nectarlink-core/src/identity.rs). The wrapping key is derived from/etc/machine-idand the uid. Any local user can read both, and the key is derived the same way again after every reboot, so the keyring adds nothing: anyone who can read the key file can rebuild the key. The type is also namedSecretServiceKeyProtectorbut never talks to the Secret Service. Please either store a random key in the real Secret Service (libsecret /org.freedesktop.secrets), falling back to plain + 0600 when there isn't one, or keep plain + 0600 and name it for what it is. -
Windows gets weaker (
identity.rs,decodeandunprotect_with_known). A Windows build now accepts a plain (method 0) key file where it used to refuse one, so swapping the key file for an unprotected one would go unnoticed. Please limit the fallback to the migration it's meant for (Linux, method 0 to 2), and re-seal with the build's own protector after loading. -
Rounded images lose their corners on Windows too (
qml/components/RoundedImage.qml). The summary says the mask became a Shape + texture fill, but the code only setsclip: true, so album art and photos are square everywhere. Please keep the MultiEffect mask whereQtQuick.Effectsis available, and use the fallback only where it isn't. -
Deck launch tiles get around the commands opt-in (
core/nectarlink-core/src/deck.rs).LaunchAppnow accepts.shscripts and any absolute Unix path. Windows refuses.batand.ps1for this action on purpose: scripts belong toRunCommand, behind the off-by-default "Run Deck shell commands" toggle. Please keep.desktop,.AppImageand plain executables, and drop.shand arbitrary paths (or send them throughRunCommand). -
Real clipboard text gets dropped (
core/nectarlink-core/src/timeline.rs, used byclipboard.rs).is_likely_binary_or_corrupted_image_textrejects any clip whose first 512 characters mention "Exif", "IHDR", "IDAT", "JFIF" or "ICC_PROFILE", which catches a photographer's notes or code that reads PNGs. It runs in the core, so it affects every platform. YourPhoneClipboard.ktchange fixes the garbage where it starts, so I'd remove the core check. If you'd rather keep it, keep only file signatures at the very start plus the replacement-character ratio, with tests for normal text that mentions those words. -
Reconnecting after a network change gets slower (
node.rsacceptandsession.rshandshake_listener). Both now refuse an incoming connection whenever this side's own outgoing session still looks alive. When the other device changes networks and dials again, this side's old session is already dead but not yet detected, so the new connection is refused until the heartbeat gives up (several seconds), and the duplicate tie-break inregister_sessionnever runs. Please leave that decision toregister_session, or refuse only when the existing session has received something in the last second or two. These changes also need a two-device test (core/nectarlink-core/tests/two_devices.rs). None of thenode.rs/session.rschanges have one yet.
Worth changing
- Windows title-bar buttons (
CaptionButtons.qml): the hand-drawn paths also replace Windows 11's own Segoe Fluent glyphs on Windows, and the maximize button is now also registered as a caption hole, which may stop Snap Layouts from appearing when hovering it. Please keep the glyphs and the current maximize handling on Windows, and use the paths elsewhere. - The
.qmcompiler inbuild.rs: a second implementation of Qt's binary format to keep in step. I'd rather requirelrelease(packaging already does) and fail with a clear message when it's missing. preferredRendererType = 2inComponent.onCompleted(Icon, Spinner, App, HomePage, SadBee): a magic number, and on Windows the first frame now draws with the default renderer. A file selector or a small Linux variant of these components would keep Windows as it is.- Android clipboard (
isImageUriByMagic): it opens every copied content link to check whether it's an image. That's fine as a fallback, but please skip it when the clip's description already says what it is. - Window geometry persistence: new behavior on Windows. Please check it with two monitors, and when the monitor it was saved on is gone.
Good as they are
handoff.rs: no more panic on multi-byte text. That's a real crash, and it comes with a test.features.rs: Linux as a desktop OS, with a test that the feature matrix matches Windows.session.rsrequest: the timeout now covers sending too.node.rs: USB preferred over LAN in the duplicate tie-break, loopback addresses no longer persisted, cancelled dials closed, andconnected()waking the supervisor.- FFI: sending state again after the event channel lags.
try_send_realtimefor PC audio frames.mainnow also sends the control stream ahead of media streams, and the two fit together.
How to move this forward
At 106 files and 19,000 lines this is hard to review in one go and hard to keep rebased. Could you split it?
- PR A, shared changes: core, FFI, Android, and the desktop code and QML that also run on Windows, with the fixes above and a two-device test for the session and dial changes. It's small enough to review properly, and it merges first.
- PR B, Linux: the Linux-only code on top of A. Please add a CI job that builds the desktop app on Ubuntu, so Linux keeps building after it's merged. Today CI builds only the core there.
Both on top of the current main, please (it moved by 47 commits today, as mentioned above). Thanks again, this is a big step for the project.
|
Hi @thtbee Thank you very much for taking the time to review this huge amount of information. Appreciate your time. I'll start with the rebase onto main, and then your input to improve the code, in particular the commonly shared and Windows related parts. Afterwards, I'll see how this can be split into two pull requests. In general, it shall be feasible given that the Linux sources have there own folder. I'll see about that. And yes, the automated build shall be extended to provide all packages. Again, thanks for your time and I'm looking forward to seeing this fully merged soon. Cheers |
77f3ab0 to
25f069f
Compare
|
Thanks for the thorough review @thtbee! I have rebased onto
Summary of Review FixesMust-Change Items
Worth-Changing Items
Kindly let me know whether this fits your requirements. Cheers. |
25f069f to
ad85c46
Compare
|
Hi @thtbee Going to check what's broken here and address it accordingly. No worries, I'm on it. Cheers |
ad85c46 to
b712298
Compare
91ad453 to
6b5bd24
Compare
|
Hi @thtbee Back to draft as I want to handle 6c71204 first properly and don't want to risk an early PR merge. This shall be helpful to keep the right sequence of PRs in place. Minor delay but I won't touch the code base for now, just taking care of the intermediate branch Hope that's OK with you. Cheers |
6b5bd24 to
a5f5285
Compare
94d272e to
40add6b
Compare
From @jochenkirstaetter: the parts of the Linux port that the core and the Windows app share, plus fixes for USB sessions, split out of #12 so they can land first. Core: - features.rs: a "linux" device of unknown kind is a desktop, and DeviceFacts::for_os builds facts for an OS, with a test that Linux gets the same feature matrix as Windows. - deck.rs: launch tiles also take .desktop and .AppImage launchers and absolute or ~/ Unix paths. The check is about the path's shape only, so a saved tile is kept or dropped the same way however the app starts: no "..", no shells or .sh scripts, nothing under /etc, /tmp, /var/tmp, /dev, /proc or /sys. The PC expands ~/ when it launches. - identity.rs: the key file is written 0600 on Unix. - handoff.rs: no panic on text with multi-byte characters at the byte positions it slices. - node.rs: - A USB tunnel to a paired device starts and wakes its supervisor rather than dialing on the side, and a LAN dial in flight gives way when a tunnel comes up (dial_with_usb_interrupt). A dial that's dropped closes its connection (CloseOnDrop). - The higher ID skips its 750 ms wait when a tunnel is already there. - register_session skips connections that are already closed, and publish_online only reports a session that's still alive. - Loopback addresses from USB tunnels are no longer saved as a device's last addresses. - The event channel holds 2048 events instead of 512. - session.rs: PC audio sent over the control stream drops frames when the outbox is half full (try_send_realtime), so it can't hold up heartbeats and requests. Session::request's timeout covers sending as well as the reply. - A two-device test for a LAN session moving to USB, audio frames dropped under load while a ring gets through, and reconnecting after a restart. FFI: after the event stream lags, the app gets fresh link, wake, info, capability and queue state for every paired device. PC app: - file_url handles Unix paths and keeps Windows drive and UNC paths as they were. - The local API token comes from win::secure_random. - Off Windows: no DWM backdrop, vector caption glyphs instead of Segoe Fluent Icons, and Linux wording and file filters for launch tiles. Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>
… Ubuntu 24.04 Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>
Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>
…ual mic hookup Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>
…on for Command Palette Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>
ff9da23 to
abd635e
Compare
thtbee
left a comment
There was a problem hiding this comment.
Hi @jochenkirstaetter, thanks for this, and for rebasing it onto #23 so quickly. It's a huge amount of work, and a lot of it is careful: the virtual microphone already lives in $XDG_RUNTIME_DIR with a quoted, sanitized description, the updater still checks the release's SHA-256 before it installs anything, and the phone mic hooks straight into the changes from main.
I went through it file by file, starting with what Windows shares, then the Linux parts that run as root or touch files, and checked a few things against Qt 6.12 rather than guess. Here's what I found.
Must change before merge
-
Turning on a phone's storage also shares this PC's folders (
desktop/app/src/bridge/app.rs,set_toggle). Whenstorageis turned on for a device, it now callsset_share_pc_storage(true). That's the same issue as point 1 in the first #23 review: on a PC that toggle means "browse that phone here", and Your PC in your pocket has to stay its own choice. It came back through this PR; please remove those three lines. -
The
.debupdate runs as root on a file the user can swap (updater.rs, Linuxinstall_with_prompt). The download is checked against its SHA-256 in~/.cache/nectarlink/updates, thenpkexec dpkg -iruns on that same file. Anything running as the user can replace it between the check anddpkg, and its maintainer scripts then run as root. Windows avoids this on purpose: the task copies the installer to a folder only SYSTEM can write and checks it there. The Linux equivalent is to have the root step copy the file somewhere only root can write and check the hash there beforedpkgreads it. (Related: since every release has an AppImage,LINUX_FALLBACK_SUFFIXESpicks it first, so a.debinstall never gets to the.debbranch at all.) -
A Qt 6.4 guard changes Windows rendering (
IslandWindow.qml, two places).preferredRendererType = 2was meant to beShape.CurveRenderer, but I checked on Qt 6.12:Shape.CurveRendereris4, and2is the old NVPR renderer, so the Island's shapes lose curve rendering on Windows and on Linux alike.Icon.qml,Spinner.qml,HomePage.qml,CaptionButtons.qmlandSadBee.qmlstill useShape.CurveRendererdirectly, so the Qt 6.4 support is also incomplete. This is really the question from the first round: ship our pinned Qt with the Linux build, or support the distribution's Qt. If we bundle Qt (see 4), none of these guards are needed and the shared QML can stay as it is. I'd rather go that way. -
The packages don't include Qt.
build-appimage.shputs only the binary, the.qmfiles and a placeholder hexagon icon into the AppDir, andbuild-deb.shdoesn't list any Qt 6 packages inDepends. On a clean system neither starts. The AppImage needs Qt and its QML modules bundled (linuxdeploy with its Qt plugin does this), the.debneeds the Qt 6 runtime and QML packages inDependsor a bundled copy, the icon should be the real one fromassets/brand/rather than the inline hexagon, the.qmfiles belong under/usr/share/nectarlinkrather than/usr/bin, and the maintainer address should be one that reaches someone (nectarlink.orgisn't a domain the project uses). -
FFmpeg structs read at fixed offsets without a version check (
linux/h264.rs). The offsets forAVPacket(0x18,0x20) andAVFrame(0x40,0x68,0x6c,0x74) do match FFmpeg 4 to 7, but the loader also accepts a barelibavcodec.so, which can be any version, and nothing checksavcodec_version()before reading them. A future FFmpeg would mean reading the wrong memory rather than an error. Please check the major version against the ones the offsets were taken from and refuse anything else, and do the same for libopenh264 (WelsGetCodecVersion), whose hand-written structs inpc_mirror.rsalso follow one version's headers. -
The Linux CI job fails. Six tests need libraries the job doesn't install: libopenh264 for the encoder, libavcodec for the decoder, and the
ffmpegcommand for converting recordings. I'd install them in the job (ffmpegandlibopenh264-7on Ubuntu 24.04) so the tests keep running the real code rather than skipping. Two of them are still named after Media Foundation (…_with_mft,media_foundation_…) and could use neutral names now. Also, withoutffmpeg, choosing MP3 for recordings saves a different format without saying so; the app should tell the user. -
Uninstalling goes around the package manager (
setup.rs, Linuxremove). It runspkexec sh -c "rm -f /usr/bin/nectarlink-desktop ...", which leaves dpkg thinking the package is still installed. For a package install it should bedpkg -r(orapt remove), and only the tarball install should remove files by hand. Theuninstall.execheck above it can go on Linux, and the messages there ("Uninstall stopped with code …") aren't translatable yet.
Worth changing
- Phone storage on Linux is a sync loop, not a mount. Each pass lists the phone's whole tree, downloads every file up to 512 KB, never shows larger files at all, and pushes local edits back. On a phone with a large Download or WhatsApp folder that's a lot of traffic and battery, and a folder that silently leaves out big files is confusing. On Windows the files are placeholders that download when opened. On Linux, a GVfs backend or a small FUSE filesystem would give the same on-demand behavior. Either way, I'd move it into
linux/storage.rs:storage.rswent from 2cfgattributes to 81. - Wayland. Screen capture uses
XGetImage, and input and window activation usexdotool,wmctrlandxprop, which only see XWayland windows. Ubuntu and Fedora default to Wayland, so PC mirroring and remote input fail there without saying why. Until there's a portal or libei path, please report those capabilities as unavailable on Wayland so the phone doesn't offer them. - External tools. The Linux backend runs about 60 different programs. A few things stand out: remote mouse movement starts an
xdotoolprocess per event, which adds latency at 60 events a second; the tray is a Python GTK script, while Qt'sQSystemTrayIconalready speaks StatusNotifierItem on Linux; and the updater downloads throughcurl, where--proto =https --proto-redir =httpsand a--before the URL would be good to have. Whatever stays should be listed in the package'sDependsorRecommends, and the app should say when one is missing. #[allow(dead_code)]without acfginmirror.rs,pc_audio_stream.rsandcore_host.rsalso hides real dead code on Windows.#[cfg_attr(target_os = "linux", allow(dead_code))]keeps the warning there.build.rsbakes an absolute path into.qt6-sysrootasDT_RPATH(which also overridesLD_LIBRARY_PATH), hard-codesx86_64-linux-gnuand forces-fuse-ld=bfd. For packaging, a relative$ORIGINrpath (or none, with the libraries bundled) works on more systems.setup::installed()on Linux reports this program's own version, not the installed one.- Two Windows behavior changes that the description doesn't mention: the Command Palette hotkey now closes the palette when it's open, and the Shelf also shows images from received files. Both seem fine to me, but they should be in the description, or better, in their own PRs like #29 to #33.
- CLI process watching polls
/proc.pidfd_open(Linux 5.3 and later) gives a race-free wait if you want it.
Good as they are
- The virtual microphone:
$XDG_RUNTIME_DIR, the quoted and sanitizeddevice.descriptionwith the phone's name, and the test with a hostile name. - The phone mic
Sinkbuilding onACTIVEandrestart()frommain, and choosing the virtual mic as the preferred "cable". - The updater keeping the strict asset name check and the SHA-256 check.
- Storage names from the phone go through the core's
is_valid_storage_name, so the sync can't write outside its folder. - The only new crate is
libc.
How to move this forward
At almost 19,000 lines this is hard to review well in one piece, and the split you did for #29 to #33 worked really nicely. I'd suggest the same here, roughly in this order, so each part can land on its own:
- Build, packaging with a bundled Qt, and the CI job (which also settles point 3).
- Basics: tray, notifications, clipboard, autostart, single instance.
- Input and window handling, with the Wayland checks.
- Media: mirroring, audio, the mic and the camera, with the version checks from point 5.
- Phone storage.
- Updater and setup.
Happy to review each as it comes. Thanks again for pushing the Linux side this far.
…alignment and +9 dB makeup gain Signed-off-by: Jochen Kirstätter <7329802+jochenkirstaetter@users.noreply.github.com>
…pability advertising for macOS - Restore storage.rs, pc_audio_stream.rs, pc_mirror.rs, and play_on_phone.rs to single files with internal cfg blocks (matching Linux thtbee#12 architecture). - Mount macOS platform code via #[cfg(target_os = "macos")] #[path = "mac/mod.rs"] mod win in main.rs and remove crate-wide allow(unused) attributes from desktop and spike crates. - Remove mirror.view from BASE_CAPABILITIES in core so non-decoding desktop platforms can cleanly opt out. - Restrict Windows-only capabilities (media.control, storage.mount, etc.) to cfg(windows) in core_host.rs and report std::env::consts::OS. - Implement real QImage bitmap decoding and AppleScript pasteboard image copy with automatic format conversion and collision-free temporary files. - Return explicit unsupported errors for H.264 video decoding, MP4 recordings, and streamed audio playback instead of silent success stubs. - Fix AppleScript speaker volume and mute status query parser. - Add capability matrix unit test in core and live capability assertion test in desktop. - Add macOS platform support summary and build requirements to README.md.
…pability advertising for macOS - Restore storage.rs, pc_audio_stream.rs, pc_mirror.rs, and play_on_phone.rs to single files with internal cfg blocks (matching Linux thtbee#12 architecture). - Mount macOS platform code via #[cfg(target_os = "macos")] #[path = "mac/mod.rs"] mod win in main.rs and remove crate-wide allow(unused) attributes from desktop and spike crates. - Remove mirror.view from BASE_CAPABILITIES in core so non-decoding desktop platforms can cleanly opt out. - Restrict Windows-only capabilities (media.control, storage.mount, etc.) to cfg(windows) in core_host.rs and report std::env::consts::OS. - Implement real QImage bitmap decoding and AppleScript pasteboard image copy with automatic format conversion and collision-free temporary files. - Return explicit unsupported errors for H.264 video decoding, MP4 recordings, and streamed audio playback instead of silent success stubs. - Fix AppleScript speaker volume and mute status query parser. - Add capability matrix unit test in core and live capability assertion test in desktop. - Add macOS platform support summary and build requirements to README.md. Signed-off-by: saivinay1805 <saivinayreddyvavilla@gmail.com>
|
Hey @thtbee and hello https://github.com/saivinay1805 As suggested, I fanned out this mega PR into six (6) smaller, fully isolated and stacked milestones. Rebasing shall be relatively straight-forward. Cheers |
…pability advertising for macOS - Restore storage.rs, pc_audio_stream.rs, pc_mirror.rs, and play_on_phone.rs to single files with internal cfg blocks (matching Linux thtbee#12 architecture). - Mount macOS platform code via #[cfg(target_os = "macos")] #[path = "mac/mod.rs"] mod win in main.rs and remove crate-wide allow(unused) attributes from desktop and spike crates. - Remove mirror.view from BASE_CAPABILITIES in core so non-decoding desktop platforms can cleanly opt out. - Restrict Windows-only capabilities (media.control, storage.mount, etc.) to cfg(windows) in core_host.rs and report std::env::consts::OS. - Implement real QImage bitmap decoding and AppleScript pasteboard image copy with automatic format conversion and collision-free temporary files. - Return explicit unsupported errors for H.264 video decoding, MP4 recordings, and streamed audio playback instead of silent success stubs. - Fix AppleScript speaker volume and mute status query parser. - Add capability matrix unit test in core and live capability assertion test in desktop. - Add macOS platform support summary and build requirements to README.md. Signed-off-by: saivinay1805 <saivinayreddyvavilla@gmail.com>
What and why
Provides the full NectarLink desktop and CLI experience on Linux (
desktop/app/src/linux/,desktop/packaging/linux/,core/nectarlink-clilinux_watch, andubuntu-latestCI job).Summary of Linux-specific changes (
25f069fon top of #23)desktop/app/src/linux/*— 31 modules):audio.rs,audio_out.rs), autostart (autostart.rs), BLE proximity (ble_beacon.rs), browser tab handoff (browser.rs), Bluetooth HFP (bt_hfp.rs), Wayland/X11 clipboard (clipboard.rs), diagnostics (doctor.rs), polkit elevation (elevated.rs), H.264/MP4 (h264.rs,mp4.rs), HTTP/updater helper (http.rs), icon/image helpers (icon.rs,image.rs), input injection (input.rs), XDG desktop actions (jumplist.rs), MPRIS2 media control (media_sessions.rs,smtc.rs), Netlink network watcher (net.rs), share target (share_target.rs), shell integration (shell.rs,shortcut.rs),flocksingle-instance IPC (single_instance.rs), Freedesktop notifications (toast.rs), StatusNotifierItem tray (tray.rs), V4L2/PipeWire virtual webcam & microphone (vcam.rs,vmic.rs), wallpaper (wallpaper.rs), widget state (widget.rs), per-window capture (window_capture.rs), and Wake-on-LAN (wol.rs).core/nectarlink-cli/src/main.rs):linux_watch(pidfd_open+/proc/<pid>/stat) andDesktopDirIPC fornectarlink notify-when.desktop/app/cpp/native_window.cpp):#elsepath for Linux using QtstartSystemMove()andstartSystemResize().desktop/packaging/linux/*):build-tarball.sh,build-deb.sh,build-appimage.sh,nectarlink.desktop, andorg.nectarlink.desktop.metainfo.xml..github/workflows/ci.yml):desktop-linux(Linux app (x64)onubuntu-latest) runningcargo clippy,cargo test, andcargo build --releasefornectarlink-desktop,nectarlink-vcam, ands1-qt-rust.How it was tested
cargo fmt --all --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace(626 Rust unit and integration tests across all crates)cargo xtask tokens --check&cargo xtask strings --check./scripts/check-headers.ps1(SPDX license header verification)./android/gradlew -p android testDebugUnitTest0.1.4) over both USB and LAN.Checklist
./scripts/check.ps1passes (or the CI equivalent for the parts I changed)git commit -s), see CONTRIBUTING.md