15 KiB
Android parity and issue ownership
Contract for #88, reviewed 2026-10-08 against apple/Sources/App/IronStorageApp.swift, apple/Sources/AutoFill/CredentialProviderViewController.swift, crates/apple/src/lib.rs, and crates/storage/src/mobile*.rs. See architecture.md for support, dependency, security and update policy.
Status: #88 records the architecture baseline. The Android shell (#89), native packaging (#92), shared contract (#91), and private runtime (#90) are implemented; feature presentation rows remain assigned below. Issue comments record per-issue validation and outstanding test gaps. #111 must attach the final screen/component/test evidence to each row before claiming full parity. Every issue linked here belongs to milestone 23.
Current iPhone and shared-mobile capabilities
| Existing capability / source | Owning Rust API/module | Android presentation / approved API | Implementation and required evidence |
|---|---|---|---|
| Five destinations: Home, Passwords, Search, TOTP, Preferences; loading/empty/ready/locked/error fixtures (AppContext, ShellViewController) | mobile::MobileShell, MobileTab, MobileShellState; config selected tab | Single Activity, Compose Material 3 Adaptive, Navigation 3, ViewModel/StateFlow | #89/#91/#102; shell fixtures, startup, back/navigation and recreation |
| Local-first setup, generated/imported key, passphrase confirmation, staged QR import (SetupChoiceViewController, LocalSetupViewController) | mobile_onboarding::MobileOnboardingOperation, crypto; mobile_key_transfer import | Compose forms and core facade; OS input/camera adapters | #95/#106; real local init, key export/import, canceled/failed setup rollback |
| Optional HTTPS clone, server/account/token, repository/branch discovery; attach remote to local store (OnboardingViewController) | mobile_onboarding::{MobileOnboardingRequest, MobileOnboardingOperation}; git/config/secret_store | Forms, typed incremental progress and cancel | #95; TLS/auth/branch/cancel/offline errors; repository remote authority |
| Home history, ahead/behind, local/remote change status, commit activity (ShellViewController, MobileCommitActivityViewController) | mobile_home::MobileHomePage/Operation and git | Compose Home/list/detail | #97; summaries and stale/error state, Git identity, author/history |
| Commit, Fetch, Pull, Push and pull-then-push Sync | mobile_home::MobileHomeOperation; git sync/merge/conflicts/signing | Typed actions, progress/cancel, conflict presentation | #97; pull failure prevents push, conflict/data-loss cases and signing |
| Directory hierarchy, breadcrumbs, folder and entry rows (PasswordDirectoryViewController) | mobile_passwords::MobilePasswordPage/Row/RowKind; repository/read | Adaptive list/detail with stable IDs | #96; deep tree, empty/error, keyboard and size changes |
| Password-store name search (PasswordSearchViewController) | mobile_passwords::MobilePasswordPage::search | Search ViewModel, Flow latest-query-wins | #101; large-store responsiveness and canceled/stale results |
| Unlock entry, explicit user activity, timeout, manual/background lock; shared entry/TOTP/editor lease (LockedPasswordViewController, AppContext) | mobile_authentication::MobileAuthentication; authentication generation/leases | AndroidX BiometricPrompt/CryptoObject, KeyguardManager, lifecycle | #93/#98/#100; wrong passphrase, cancel, expiry, generation race, changed enrollment and process death |
| Structured fields/sections, masked secrets, explicit reveal/select/copy; OTP field diagnostics (MobileEntryFieldCell) | mobile_entry::MobileEntryPage/Field; mobile_authentication::{entry_page,reveal_entry_field,copy_entry_field} | Compose viewer, sensitive controls and accessibility semantics | #99/#100; no pre-unlock values, relock clears all views, copy expiry/ownership |
| Create entry/folder, structured multiline editing, add/remove/reorder fields, dirty discard confirmation (MobileEntryEditorViewController) | mobile_entry::MobileEntryDraft; mobile_authentication editor session/create/save/discard; write | Compose editor/forms, stable field IDs | #103; actual encrypted saves, conflict tokens, interrupted saves and dirty navigation |
| Password length/generation and replacement | generate; mobile_entry::MobileEntryDraft::generate_password | Generator controls call Rust | #103; Rust validation and unbiased generated value; no Kotlin generator |
| OTP URI input, camera/image import, review before Save (OtpImportViewController; TOTP Add action) | mobile_entry::MobileEntryDraft::add_otp_image; document/otp/presentation | CameraX frames or Android photo picker bytes; Rust decode and field construction | #103/#106/#107; URI/image/scan parity, invalid/oversized input, cancel; no payload parsing in Kotlin |
| Move/copy/delete entries and folders, destination browse, overwrite and dirty-editor handling (PasswordMutationCoordinator) | mobile_mutation::MobileMutationService/Plan/Request; mutation | Compose dialogs/destination picker | #108; revisions, destination conflicts, recipient reencryption and rollback |
| TOTP discovery catalog, incremental progress, warm cache, refresh, cancel/resume, cached search (TotpListViewController) | mobile_totp discovery/catalog/operation; mobile_authentication cached/search APIs | Compose list/progress/search; private no-backup catalog | #90/#110; cold/warm/change/add/remove/corrupt/wrong-store/concurrent-change tests |
| TOTP rows, large detail code, countdown/expiry, copying (TotpDetailViewController, TotpCodeView) | mobile_totp::MobileTotpPage/Detail; otp; mobile_authentication::{totp_detail,copy_totp_code} | Compose code/detail; OS clock facts, Rust timing | #107/#100; RFC vectors, boundary countdown, stale callback/relock and clipboard |
| Repository/server identity, local-only/add-remote Settings, token replace/remove (PreferencesViewController, TokenUpdateViewController) | mobile_authentication::MobilePreferences; mobile_onboarding::replace_configured_application_token; config/secret_store | Compose Settings/facade | #104/#95; no token display; protected replacement/removal and offline operation |
| Key identity/fingerprint, GPG recovery import and public/private QR export (KeyImportScannerViewController, KeyExportListViewController, KeyQrExportViewController) | mobile_key_transfer::{MobileKeyTransferService,MobileKeyTransferImport}; crypto/presentation | CameraX Preview/ImageAnalysis; animated Rust-provided matrices | #106/#104; multi-frame order/duplicates/interruption, bounded frames, private export authorization and camera denial |
| Inactivity timeout, biometric enable/disable, Lock Now, appearance and Git commit name/email | mobile_authentication preference setters, git_identity; config | Compose Settings, BiometricPrompt, system/light/dark theme | #98/#104; persistent settings, validation, Keystore enrollment/recovery and lease revocation |
| iPhone AutoFill credential selection (CredentialProviderViewController) and shared identity/access domain | desktop/read/document/secret_store today; storage-owned provider contract to add | AndroidX CredentialProviderService (API 34+) and framework AutofillService (API 26+) | #109; typed caller/site identity, non-secret candidates, explicit selection/auth, forged/stale requests, permissions and cross-process lifetime |
| Camera permissions, private paths, secret-store mechanics, clipboard, scene/process events, screen privacy | Rust repository/config/secret_store/authentication/presentation; platform callbacks contain facts only | Android framework adapters, FLAG_SECURE, backup rules, ClipboardManager | #90/#93/#98/#100; backup inspection, sandbox paths, pressure/update, screen/clipboard/lifecycle tests |
| Accessible labels/actions, large text, keyboard, responsive layout and localization | Typed Rust state/actions; UI accessibility is Kotlin | Compose semantics, TalkBack, adaptive layout, resources | #105; measured keyboard/TalkBack/large-font/multi-window/RTL/rotation coverage |
| Watch pairing/install state in Settings; TOTP Watch selection, snapshots, receipts, transfer and watchOS detail | mobile_watch; shared bridge's explicit watch feature enabled only by crates/apple |
Absent from Android records and UI (#91) | Deliberate Watch-only exclusion; no unsupported placeholder controls or Wear implementation |
Name search matches current mobile behavior; CLI/TUI decrypted grep is not an iPhone parity claim. Android has no separate filesystem-based search. The shared core's recipient/signing rules, pass/pass-otp format, HOTP counter safety, Git failure semantics and secret-redaction requirements remain binding wherever an Android workflow invokes them; platform presentation may not replace them.
Current iPhone camera adapters pass OS-decoded QR bytes. Android #106 supplies bounded Y-plane/stride/orientation facts through the shared native contract; storage decodes and reassembles the transfer. See key-transfer.md for interruption/security behavior and evidence. The external-credential matching/grant API required by #109 must be implemented in storage, rather than copying frontend selection assumptions from AutoFill.
Complete milestone issue-to-layer map
Links identify the live issues by number and a concise description. Ordering follows the user's label priority, then numeric order; do not substitute a dependency from another milestone. This list gives ownership, not permission to skip acceptance criteria.
| Issue | Owning layer | Approved implementation/API and completion gate |
|---|---|---|
| #88 Architecture/support/dependency policy | Repository architecture decision | This ADR and parity/owner matrix; review against current sources |
| #89 Gradle scaffold/module boundaries | Android build/app/core/platform/bridge modules | Kotlin DSL/catalog/wrapper, Compose/Hilt/KSP, source/dependency gates, all shell fixtures; clean debug APK |
| #90 Private paths/config/cache/backup | crates/storage plus Android path adapter | Context roots/capabilities, atomic layout/migrations, backup rules; Rust and sandbox/update tests |
| #91 Platform-neutral mobile contract | crates/storage and shared UniFFI bridge | Typed semantic actions/icons, opt-in Watch data, platform callbacks, shared Swift/Kotlin inventory tests |
| #92 Native Rust/UniFFI packaging | Shared/Android Rust bridge and Gradle rust-bridge | cargo-ndk, generated immutable Kotlin/JNA Android, both ARM ABIs, checksums/16 KB/loading/debug symbols |
| #93 Keystore adapter | Delivered: Rust sealed-record orchestration and Android OS adapter | Keystore policy and recovery; native AES-GCM/CryptoObject and Rust lifecycle tests |
| #94 CI/static analysis/reproducibility | Build/CI/test infrastructure | Cargo gates, Gradle build/Lint/ktlint, JVM/Compose/instrumented tests, verification/locks/SBOM/ABI checks |
| #95 Local-first/HTTPS onboarding | storage mobile_onboarding plus Compose features | Existing Rust initialization/discovery/clone/credentials; no Kotlin transport |
| #96 Hierarchical navigation | storage mobile_passwords plus Compose features | Material 3 Adaptive list/detail, typed rows/stable IDs |
| #97 Home/Git | storage mobile_home/git plus Compose features | Rust actions/history/progress/conflicts; Compose Home and activity |
| #98 Biometric authentication lease | storage authentication/mobile_authentication plus platform adapter | AndroidX BiometricPrompt/CryptoObject, KeyguardManager; Rust generations/policy |
| #99 Structured viewer | storage mobile_entry/mobile_authentication plus Compose features | Typed fields/sections/reveal/copy, Rust OTP and diagnostics |
| #100 Privacy/lifecycle/plaintext | storage lease/presentation plus platform/app | Lifecycle, FLAG_SECURE, ClipboardManager, no saved plaintext/logs, race tests |
| #101 Name search | storage mobile_passwords plus Compose features | Rust cached/name search, Flow cancellation/latest query wins |
| #102 Adaptive shell | storage mobile shell contract plus Android app/features | Navigation 3/Material 3 Adaptive, StateFlow, restoration without secrets |
| #103 Entry/folder/editor/generation | storage mobile_entry/write/generate plus Compose features | Rust editor sessions/drafts/atomic saves, structured multiline controls and OTP import |
| #104 Settings/recovery | storage config/mobile_authentication/key transfer plus Compose features | Shared setters, token/key recovery, Biometric/appearance/identity controls without Watch |
| #105 Accessibility/adaptive/localization/performance | Android features/resources plus storage cancellation | Compose semantics/TalkBack/resources, measured Macrobenchmark/profiles if justified |
| #106 CameraX GPG QR import/export | storage QR/key transfer plus platform camera/Compose | CameraX frames, Rust decode/envelope/matrices, permission and bounded payload checks |
| #107 TOTP list/detail | storage mobile_totp/otp plus Compose features | Rust codes/timing, large display/copy/refresh, OTP creation/import navigation |
| #108 Move/copy/delete | storage mobile_mutation/mutation plus Compose features | Rust plans/tokens/overwrite/reencryption, destination and confirmation UI |
| #109 Credential Manager/Autofill | storage identity/permission/credential contract plus OS services | AndroidX provider and framework AutofillService, explicit selection/lease before secrets |
| #110 TOTP catalog/discovery/search | storage mobile_totp plus Compose features | Existing hash/path/is_totp catalog, Rust progress/checkpoints/filter, no Watch |
| #111 Parity/security/reliability audit | All above plus independent acceptance review | Completed matrix evidence, Rust/Android release gates and emulator threat tests |
| #112 Google Play distribution | Release tooling/listing and production app | Protected signing/Play AAB, audited manifest/licenses, emulator install and update-preservation tests |
#87 is the closed umbrella issue, not an open implementation candidate. Verify live issues after each closure; newly created bug/critical issues may change the order. #91 and #90 currently name later packaging/contract dependencies; their implementations may establish the necessary narrow Rust APIs before consuming full Android packaging, but they must meet all stated acceptance tests before closure. Do not close a partly integrated issue just because its unit tests pass.