Skip to content

Releases: grkndev/LinkToMac

LinkToMac 0.9.0

Choose a tag to compare

@grkndev grkndev released this 15 Sep 16:35

Both apps update this time: Android 0.8.5 → 0.9.0, Mac 0.8.5 → 0.9.0. Install both — file transfer needs each side.

Send files, both ways

Until now the only thing that crossed between the two devices was the clipboard, and an image had to be squeezed under a 1 MiB frame to get there. Files are now split into chunks and acknowledged one by one, so the frame cap isn't the size limit any more.

From the phone

Share anything, and the share sheet has two new entries:

  • Send to Clipboard — for images. Downscaled to fit and dropped straight on the Mac's clipboard, ready to paste.
  • Send as a File — for anything. Original quality, saved on the Mac.

Both also appear in the sheet's top row, next to Quick Share's devices, so it's one tap from a photo.

Share a PDF or a zip and only Send as a File shows up — the clipboard entry is for things you can actually paste. Neither one opens the app; a notification tracks the transfer and tells you how it went.

From the Mac

  • Drop a file on the dashboard window — the whole screen turns into the drop target.
  • Drop it on the menu-bar icon — the panel opens with a drop zone, so this works with the window closed.

Files land in Downloads/Link to Mac on the phone (tap the notification to open one), and in the folder you choose under Settings ▸ Files on the Mac — ~/Downloads/Link to Mac unless you change it.

You can watch it go

Android shows a progress notification both when sending and when receiving. The Mac shows a percentage in the drop zone while sending, and a row on the dashboard while receiving.

"Sent" is a real delivery receipt, not a guess: the sender waits for the other device to acknowledge every single chunk before it says so.

Also fixed

  • A full-resolution photo no longer risks taking the phone's background service down. Resizing a modern camera image for the clipboard could exhaust memory; it's now decoded within a bound.
  • Fewer images rejected as too large. Photos that used to fail with "can't fit under the frame budget" now compress far enough to go.
  • The Mac's name reaches the phone, so the phone's share entries can say which Mac they mean.

Known limitations

  • Finder ▸ Share ▸ "Send to Phone" does not work. The extension ships inside the app and even shows up in the Share menu, but macOS refuses to run an app extension that isn't signed with an Apple Developer certificate — and this app is deliberately unsigned. Clicking it does nothing. Use drag-and-drop instead. The code stays in the bundle and will work the day the app has a signing identity.
  • 64 MB per file. Anything larger is refused with a message rather than failing halfway.
  • Transfers are one file at a time; multi-select isn't supported yet.

Install

Android: sideload linktomac-preview-0.9.0.apk.
macOS: existing installs update themselves via Sparkle; new installs open LinkToMac-0.9.0.dmg (right-click → Open, since the app is unsigned).

LinkToMac 0.8.5

Choose a tag to compare

@grkndev grkndev released this 13 Sep 09:34

Both apps update this time: Android 0.8.4 → 0.8.5, Mac 0.8.3 → 0.8.5. Install both — the new reply feature needs each side.

Android — the app no longer depends on ADB to work

Self-ADB buys exactly one thing: automatic clipboard capture. Reading the clipboard in the background is gated by Android itself, and the shell daemon behind that gate is what needs ADB, which needs Wireless Debugging, which needs Wi-Fi.

Everything else — the Mac link, remote lock, battery, notification and message mirroring, and Mac→phone clipboard — never needed it. But the app used to hard-gate on it, so a pairing that can't even be attempted (on Mobile Hotspot, Wireless Debugging can never come up) or that adbd silently forgot across a Samsung reboot took the whole app down with it: a full-screen dead end with no way past.

Now ADB is a capability, not a precondition. If it's down, the app works and only automatic capture is off — reported in Settings → Automatic capture, nowhere else.

Sending a copy without ADB

Two new paths, neither needing ADB, Wi-Fi or any permission:

  • Text-selection toolbar — select text anywhere and tap Link to Mac, next to Copy and Share. The selection goes straight to the Mac; the clipboard is never involved.
  • Notification action — Send clipboard on the ongoing notification. This is the one that also catches copies made by an in-app "Copy link" button, where there's no selection to hang a toolbar off. It briefly takes focus (that focus is the read permission) but no longer opens the app.

Password fields and some custom views won't show the toolbar entry — that's an Android restriction, not a bug.

Also fixed

  • Mac→phone clipboard no longer needs the daemon. With the daemon dead, every clip the Mac sent was silently dropped while the log claimed success. It now writes the clipboard directly.
  • Logs are readable again — a missing daemon used to log a retry every 3 seconds and bury everything else.

Both — reply to messages from the Mac

The Messages tab can now send a reply, which the phone puts on the real SMS stack. Needs the SEND_SMS permission, requested the first time you try (Settings → Grant SMS Send Access). 1:1 threads only.

Since the app usually isn't the default SMS handler, it often can't write its own sent message into the SMS store — the Mac then keeps its own copy of the reply as the only record.

Install

  • Android (linktomac-preview-0.8.5.apk): sideload. This changes native code, so it is not an EAS OTA update — reinstall the APK. The clipboard daemon is unchanged, so no daemon restart is needed.
  • Mac (LinkToMac-0.8.5.dmg): Sparkle will offer it, or download directly. Ad-hoc signed and not notarized — first launch on a new Mac needs right-click → Open.

LinkToMac 0.8.4

Choose a tag to compare

@grkndev grkndev released this 23 Jul 09:57

Android-only patch release. No Mac update — the Mac app stays 0.8.3, and there are no wire-protocol changes, so a 0.8.4 phone and any 0.8.x Mac remain fully compatible.

Android — security fix (#32)

The privileged clipboard daemon no longer reads content on another app's behalf (confused-deputy fix).

When you copy an image, the shell-UID clipboard daemon reads it off the clipboard using shell's elevated privilege — which can reach content providers a normal app can't (MediaStore, Contacts, …). A hostile app installed on the same phone could put a forged image/* clip on the clipboard pointing at content it has no permission to read, and have the daemon read those bytes and sync them to your Mac — a privilege-escalation leak.

The daemon now resolves which app placed the clip (getPrimaryClipSource) and reads the image only if that app could itself read the URI:

  • it holds an explicit URI grant / the provider's read-permission, or
  • it owns the backing provider (its own FileProvider), or
  • for MediaStore images, it holds a READ_MEDIA_* permission.

This blocks the escalation while leaving every legitimate copy (gallery, browser, screenshots) working. It fails closed — if the source can't be verified, the image is skipped and text sync is unaffected.

Install

  • Android (linktomac-preview-0.8.4.apk): sideload (dev-client style install; not Play-distributed). This changes native code, so it is not delivered as an EAS OTA update — reinstall the APK (or push it over adb), then force-restart the daemon (Daemon Log → Restart) so the new dex actually loads (a live daemon survives reinstall).
  • Mac: no update — stay on 0.8.3.

LinkToMac 0.8.3

Choose a tag to compare

@grkndev grkndev released this 22 Jul 20:14

Combined Android + Mac release. No wire-protocol changes — a 0.8.x Mac and 0.8.x phone stay fully compatible regardless of exact patch version.

Android — reliability fixes (issues #29, #30, #31)

Three issues that together made the app unusable in common real-world conditions.

  • The clipboard daemon now self-heals from an OS memory-pressure kill (#31). One UI/lmkd can and does kill the privileged dex daemon under memory pressure with no reboot. It's launched wrapped in a tiny shell-UID supervisor loop: if the daemon dies, the supervisor respawns it in ~3s with the same port and secret — no ADB, no Wi-Fi, no user interaction.
  • "Try again" on the Reconnect screen no longer hangs forever (#30). adb.connect()'s TLS handshake had no timeout and could silently freeze the retry button forever. It's now properly bounded, and the button shows a spinner while a retry is actually in flight.
  • Mobile Hotspot no longer locks you out of the whole app (#29). Wireless Debugging can't run while the phone is a Wi-Fi hotspot, so hitting the ADB reconnect path used to strand the entire UI behind a full-screen gate even though remote lock, battery telemetry, and notification/SMS mirroring kept working underneath. The app now gives the daemon a fair chance to reattach on a cold launch, and shows a small dismissible banner instead of a hard lock-out whenever the phone↔Mac link is alive.

Mac — small reliability/polish fixes (issue #23)

  • Notification banners no longer silently drop the very first mirrored notification if it races the initial permission prompt.
  • If notification permission is denied, Settings now shows an "Open Notification Settings…" affordance instead of a toggle that silently no-ops forever.
  • Opening the dashboard at launch (e.g. as a background "Start at login" item) no longer steals focus from whatever app you're already using; Dock reopen and the menu panel's "Open Window…" still bring it to front as expected.
  • A couple of latent (never-triggered) force-unwrap crash risks in the Messages tab were hardened defensively.

Install

  • Android (linktomac-preview-0.8.3.apk): sideload (dev-client style install; not Play-distributed). This release changes native code, so it's not delivered as an EAS OTA update — reinstall or push the APK over adb.
  • Mac (LinkToMac-0.8.3.dmg): ad-hoc signed, not notarized → first launch is right-click → Open (or xattr -dr com.apple.quarantine /Applications/LinkToMac.app). Existing installs update automatically via Sparkle.

LinkToMac 0.8.1

Choose a tag to compare

@grkndev grkndev released this 17 Jul 18:18

Hotfix over 0.8.0 — supersedes it (0.8.0 is retracted to pre-release).

What 0.8.1 fixes

Fresh-install boot no longer gets stuck on the Reconnect screen. In 0.8.0, autoStart's connect+deploy path could fail with IOException: Stream Closed: the daemon-restart fix ran libadb's connect() on a throwaway worker thread, and libadb ties a connection to the thread that created it — so the connection died the instant that thread exited, and the next step (pushing the dex over adb) hit a closed stream. Reverted to the synchronous connect the app shipped for months; the Restart-button hang that 0.8.0 originally addressed stays fixed via the JS-side timeouts. (Only fresh installs were affected — an already-running daemon took a different path.)

⚠️ Update both apps together

0.8.x removes the legacy v1 encryption fallback. A 0.8.x phone and a pre-0.8.0 Mac (or vice-versa) will not sync — frames fail closed. Install the Mac and Android builds from this release together.

Everything else (carried from 0.8.0)

  • Reliability: Mac remote-lock falls back if the private lock call fails; clipboard copies in the first seconds after service start are queued, not dropped; daemon logs a build-stamped dex version on every start.
  • Security: fixed-length hashed relay-token compare; room-id + nonce/ct validation; replay-cache size cap; relay crash handlers; legacy v1 codec removed.
  • Android: truncated-dex push detection; clipboard-image temp files reaped by age; battery receiver NOT_EXPORTED; dex --min-api aligned to minSdk 30.
  • Polish: English daemon-log/restart strings; atomic Keychain pairing save; chevron indicators on Settings rows.

Install

  • Mac (LinkToMac-0.8.1.dmg): ad-hoc signed, not notarized → first launch is right-click → Open (or xattr -dr com.apple.quarantine /Applications/LinkToMac.app). Existing installs update via Sparkle.
  • Android (LinkToMac-0.8.1-preview.apk): sideload (dev-client style install; not Play-distributed).

LinkToMac 0.8.0

LinkToMac 0.8.0 Pre-release
Pre-release

Choose a tag to compare

@grkndev grkndev released this 17 Jul 11:51

⚠️ Known issue (fresh installs): boot can get stuck on the Reconnect screen — autoStart fails with IOException: Stream Closed (a bug in the daemon-restart fix). A 0.8.1 hotfix is incoming. Marked pre-release until then.

DO NOT USE THIS VERSION

The Android app included in this version is not functioning properly, and the app cannot be used as intended.

The bug in this version has been identified and fixed in version 0.8.1.

The faulty Android Package Kit (APK) file has been removed from this version.

LinkToMac 0.7.0

Choose a tag to compare

@grkndev grkndev released this 07 Jul 07:16

LinkToMac 0.7.0 — Clipboard images, both ways

  • Bidirectional clipboard image sync — copy an image on either device and it lands on the other's clipboard (E2E-encrypted, auto-fit under a 1 MiB cap).
  • "Send images" toggle on the phone (Settings).
  • Daemon Log screen with a Restart-daemon action and timestamped lines.
  • Fixes: clipboard history now records every image; larger WebSocket frames are no longer dropped.

Android: sideload linktomac-0.7.0-android.apk.
macOS: existing installs update via Sparkle; new installs open LinkToMac-0.7.0.dmg (right-click → Open, unsigned).

LinkToMac 0.6.1

Choose a tag to compare

@grkndev grkndev released this 06 Jul 14:35

An Android-only maintenance release: the clipboard daemon now heals itself when two installs fight over it, and the app's internals were restructured for maintainability. The Mac app is unchanged (no Mac update is offered).

📶 Reliability

  • The clipboard daemon self-heals — if the on-device daemon was started by another install of the app (e.g. a dev build next to this one), the app used to silently retry its connection every half-second forever, draining battery and flooding the logs with bridge connected, while showing everything as fine. It now detects the drop-loop, backs off, reports the daemon as unusable, and automatically redeploys it under its own key — no phone reboot needed.
  • Dev and release installs no longer conflict — each app variant now runs its clipboard daemon on its own port, so having both installed can't break clipboard sync in either.
  • Honest bridge logs — a connection the daemon drops immediately is now logged with its lifetime and reason class (auth reject / eviction) instead of an endless stream of bridge connected.

🐞 Fixes

  • "Device Log" works again on One UI — reading the daemon's on-device log could fail with BufferOverflowException once the log grew; the fetched slice is now sized to fit every device.

🧹 Internal

  • The app's JavaScript was restructured into modular features (shared M3 row/screen/sheet components, feature-scoped hooks). No visual or behavior changes — screens are pixel-identical.

Install: Android — install the APK (sideload); existing installs get an in-app update prompt. Mac — nothing new this release; your 0.6.0 stays current.

LinkToMac 0.6.0

Choose a tag to compare

@grkndev grkndev released this 02 Jul 19:20

A reliability, security, and correctness pass across the whole stack — phone app, Mac app, and relay — from a full codebase review.

🔒 Security

  • Authenticated clipboard bridge — the on-device bridge to the privileged clipboard daemon now requires a per-install secret, so no other app on your phone can read your clipboard or inject clipboard content over loopback.
  • Replay protection & type binding — every encrypted frame (clipboard, remote lock, telemetry, notifications, SMS) is now bound to its type and timestamped; a captured frame can't be replayed or relabeled (ChaCha20-Poly1305 v2 envelope, ±2 min freshness window + nonce cache).
  • Pairing keys and the relay token in the Keychain are now device-only — they no longer migrate off the Mac via backup/sync.
  • The LAN peer slot is auth-gated: a stranger on your network can no longer evict your phone or occupy the connection.

📶 Reliability

  • The Mac no longer shows "linked" when the connection has silently died (Wi-Fi drop, sleep/wake) — it now detects the dead link and reconnects.
  • The phone no longer shows the Mac online during reconnection, so "Lock Mac" won't silently do nothing.
  • The clipboard bridge recovers from stuck/half-open connections instead of quietly dying.
  • LAN discovery and handshakes now time out and self-heal instead of hanging.

💬 Messages & correctness

  • SMS threads open at the newest message and follow new messages live.
  • Draft, failed, and queued texts no longer appear on the Mac as sent messages.
  • "Check for updates" honestly reports a failure instead of falsely claiming "up to date" when offline.
  • A corrupted pairing QR / key is now rejected instead of silently breaking sync.

✨ Polish & hardening

  • Notification rows update in place; the dashboard keeps your selected tab / open conversation / page when you visit Settings and come back.
  • The logs screen no longer jumps around; rapid back-to-back clipboard copies no longer echo back to the Mac.
  • Numerous leak and crash-hardening fixes across the background service.
  • Minimum Android version is now 11 — the real requirement for the wireless-pairing pipeline.

Install: Mac — download the DMG, first launch is right-click → Open (ad-hoc signed, not notarized). Existing installs auto-update via Sparkle. Android — install the APK (sideload); the app also offers this update in-app.

LinkToMac 0.5.1

Choose a tag to compare

@grkndev grkndev released this 28 Jun 16:45

v0.5.1 (2026-06-28)

Features

  • Read your phone's messages on the Mac — your Android SMS now show up on the Mac's
    dashboard Messages tab as real conversation threads (one row per contact, latest
    message previewed; tap to open the thread and read the back-and-forth in chat bubbles). Grant
    "SMS access" once on the phone (Settings → Grant SMS access) and keep the "Mirror
    messages"
    toggle on. It's read-only and one-way (the Mac can't reply yet), resolves
    contact names, and can be paused phone-side without revoking the system grant. Cleared on unpair.
  • Paged Messages & Notifications lists — both dashboard tabs now page through their history
    (7 per page, with a ‹ / › control) so a long backlog no longer overflows the window; long
    threads scroll.

Internal

  • New sms wire frame (phone → Mac), E2E-encrypted (ChaCha20-Poly1305) and forwarded opaquely by
    the relay exactly like clip/cmd/stat/note, over both LAN-direct and relay transports —
    hand-mirrored across server/ (protocol.ts/relay.ts/index.ts), mac/
    (RelayProtocol.swift), and mobile/ (RelayClient.kt/LanClient.kt). smoke.ts asserts a
    one-way sms round-trip.
  • Android: an FGS-owned SmsMirror reads content://sms (newest ~200, chunked) and observes it
    for live deltas, resolving sender names via PhoneLookup; gated by the READ_SMS/READ_CONTACTS
    runtime grants + a "Mirror messages" toggle. New JS surface: hasSmsAccess, requestSmsAccess,
    get/setSmsForwarding, syncSms.
  • Mac: RelayClient upserts inbound messages by id and groups them into conversations
    (by thread, newest-activity first); FeaturePanel's Messages tab renders the thread list +
    chat-bubble view, both paged.

Known limitations

  • The Mac's message list is in-memory (like Notifications): it clears when the Mac app
    relaunches, and the phone re-sends the history when it next sees the Mac connect.

Updating

  • macOS auto-updates via Sparkle. Android needs the new APK reinstalled — this release
    changes native code, so it is not an over-the-air (EAS Update) change.