Repository navigation
Conversation
A C++ port of sacca97/os-findmy-e's adi-core (MIT, see macadi/NOTICE.md): a Mach-O loader, a Unicorn x86_64 VM with the few pieces of macOS userland the library calls, and the CoreADI dispatcher framing. Unicorn 2.1.4 (GPL-2.0) is fetched at build time, pinned by SHA-256, and linked only into libmacadi.so. The library itself is never in the repository or the build. In iTunes 12.8.3 for Mac the package holding it is stored uncompressed and contiguous, so it is one HTTP range request of 3.5 MB; scripts/locate_mac_coreadi.py re-derives the offsets and checks both pinned hashes, and macadi-host.yml builds the core for the host and loads the library into it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JDrrt9ff7WwPepHs6B2Gx
Since 2026-10-12 Apple issues searchPartyToken only to Mac- or iPhone-minted Anisette, and the edge now refuses any client claiming Android, so the Android ADI path cannot reach Find My. EmulatedMacAnisette fetches the macOS library from Apple, provisions one emulated Mac with akd's headers, keeps its identity and store for the life of the install, and is what AppDependencies hands out. Also: read Grand Slam's hsc 409 without au as a second factor; send the routing number Apple assigned at provisioning; give CloudKit a UUID, since a Mac's device id is not one; drop the legacy adi.pb before every session, which otherwise made a freshly provisioned machine report unprovisioned (ec -80009). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JDrrt9ff7WwPepHs6B2Gx
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JDrrt9ff7WwPepHs6B2Gx
parawanderer
left a comment
There was a problem hiding this comment.
Requesting changes. Item 1 blocks the merge; 2–4 are required alongside it.
-
Existing sessions are moved onto a new machine on upgrade.
AppDependencies.java:63makesEmulatedMacAnisettethe source for every caller, so on restore_preferLocalAnisette(main.py:896) calls itsensureReady(), which downloads CoreADI and provisions a new Mac at app start, then swaps it into the restored account. That session's token was issued to the Android machine, and per #248 a token works only with Anisette from the machine that obtained it, so it should start answering 401.identityForRestore(main.py:863) also carries the old Android device id across, so Apple receives the new Mac'sX-Apple-I-MD-Malongside the AndroidX-Mme-Device-Id. Sessions established before 2026-10-12 still fetch reports today; @parawanderer's does. Fix: keepLocalAnisettefor any session it established and give the emulated Mac only to fresh sign-ins. A restored session must not provision anything. -
libmacadi.soloads in the app process with no crash guard.MacCoreAdi.java:67callsSystem.loadLibrarydirectly, and thecatch (Exception | LinkageError)atEmulatedMacAnisette.java:114cannot catch a SIGSEGV from Unicorn or from Apple's code. That is the #232 failure mode (AGENTS.md rule 4): a native crash on the sign-in screen makes the app unopenable. Run it behind the:adiprobeprocess /TryItElsewhereFirstandNativeLoadGuard, or host the emulator in its own process and call it over binder. The second option also keeps the GPL code in a separate program from the Apache-2.0 libraries and the proprietary map SDKs (item 4). -
Session provenance is lost on upgrade.
EmulatedMacAnisettestoressessionEstablishedLocallyin a new preferences file,anisette-emulated-mac(line 41), so for every existing sessionwasSessionEstablishedLocally()reads false and the machine-change warning in_preferLocalAnisettenever fires. Read the flagLocalAnisettealready wrote. This mostly goes away with item 1. -
Unicorn is accepted as GPL-2.0. Add a licence notice for it to the app or the release, and see item 2 for keeping it out of the app process. Decision and reasoning:
docs/emulated-mac-emulator-choice.md(#275). -
Tests. The pure logic here is untested:
EmulatedMacAnisette's state machine (first provisioning, the lost-store branch that registers a new machine, failure falling back to remote,adi.pbnever saved),MacIdentitygeneration and save/load round trip, and the request shapesMacProvisioningsends. Put them on the JVM with the native layer behind an interface and aFakeApkServer-style local socket for provisioning (rule 13). Add one for item 1 as well: a restored session established withLocalAnisettekeepsLocalAnisetteand never provisions. -
The C++ port has no byte-for-byte comparison against os-findmy-e's Rust
adi-core, as the PR says.scripts/adi-emulator-diff/(#275) drivesadi-corewith a frozen clock and a fixed host UUID; run the same sequence throughlibmacadi.soand diff. A mismatch on these offline calls is a port bug that a single successful sign-in will not show. -
Move the
hsc409 handling into parawanderer/FindMy.py's_gsa_authenticateinstead of wrapping_gsa_requestat runtime (main.py:343,main.py:898). malmeloo/FindMy.py#279 makes the same change upstream.
Non-blocking: MacBookPro13,2 never shipped macOS 13.1, which is what rule 11 forbids. Keeping the values os-findmy-e was observed to get searchPartyToken with is defensible; note it in MacIdentity alongside the Darwin mismatch already noted there.
Interactively co-authored by Claude Code and @parawanderer
…icle diff parawanderer#274 runs Apple's macOS CoreADI under Unicorn, which is GPL-2.0. The decision is to ship it, with a licence notice and the library in its own process, and to swap it only if somebody has a reason to. A swap is invisible to users only if the replacement produces the same bytes, so scripts/adi-emulator-diff/ puts icicle (MIT OR Apache-2.0) under os-findmy-e's adi-core and diffs it against Unicorn. Every offline call matched byte for byte; the OTP path, which needs a provisioned machine, has not been compared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #248.
Since 2026-10-12 Apple only issues the Find My token (
searchPartyToken) to a sign-in whose Anisette machine was minted by a Mac or an iPhone. An Android-minted machine getsmmeAuthTokenalone and report fetches return 401 (malmeloo/FindMy.py#278). Since about 2026-10-20 the GSA edge also returns 503 for anyX-MMe-Client-Infowhose OS token isAndroid(probe results on #261). So the Android ADI libraries can't reach Find My any more, whatever headers are sent.This runs Apple's macOS CoreADI on the phone instead, under an x86_64 emulator - the "derived library that can be run on android directly" from #248, built off sacca97/os-findmy-e.
How it works
libmacadi.so(app/src/main/cpp/macadi/): a C++ port of os-findmy-e's Rustadi-core- Mach-O loader, Unicorn x86_64 VM with the handful of macOS calls the library makes, and the CoreADI dispatcher framing. Unicorn 2.1.4 is fetched by CMake at build time, pinned by SHA-256, and only linked into this library.CoreADI.pkgpayload is stored uncompressed and contiguous, so a single HTTP range request gets it. The payload and the extracted library are both pinned by SHA-256.scripts/locate_mac_coreadi.pyre-derives the offsets (from Apple, or from a local copy of the DMG) and reads its constants from the Java sources. Nothing of Apple's is in the repo or the APK.EmulatedMacAnisettereplacesLocalAnisetteinAppDependencies. It provisions one emulated Mac withakd's headers (from os-findmy-e'sgsa.py), keeps its identity and ADI store underfiles/anisette/mac/for the life of the install, and serves the OTP, machine id and Apple-assigned routing number to the existing Python sign-in.hsc409 with noauon GSAcompleteis treated as a second factor (the pinned FindMy.py otherwise skips 2FA); the routing number comes from Java; CloudKit and the keychain session get a view of the account whosedevice_uuidis a UUID, because a Mac's device id (00006000-…) isn't one and FindMy.py's CloudKit client parses it as one.Things to decide
libmacadi.socarries GPL-2.0 code. The ported code is os-findmy-e's (MIT); its notice is inapp/src/main/cpp/macadi/NOTICE.md.0PENTAGV…, as before). Apple could start fingerprinting this; it has changed the rules four times in five weeks.LocalAnisetteis left in place but unused. Happy to remove it, or keep it as a fallback if Apple relaxes the Android rule.Testing
adi.pbin the store, and while it exists the library reports "not provisioned", so the first sign-in after provisioning sent an empty machine id (ec -80009);badly formed hexadecimal UUID string).macadi-host.yml, which range-fetches the library from Apple exactly as the app does, checks both hashes, builds the native core for the host and loads the library ("not provisioned").MacCoreAdiFetcherTest(cpio walk, universal-binary slice, refusing a server that ignores the range),test_emulated_mac.py(409 shim, routing, CloudKit device id view).Not verified: how long a provisioned emulated Mac and its token stay accepted over days; more than one account; the instrumented suite exercises none of this because it fakes the Anisette source; and a byte-for-byte comparison of the C++ port against os-findmy-e's output (the device sign-in is the evidence it behaves the same).
Credit to @sacca97 for os-findmy-e, which this is ported from, and to @hoorayman and @poterpan on #248 for establishing that a Mac-minted machine is what gets
searchPartyToken.🤖 Generated with Claude Code