FR-PLAT-AND-1 — Storage Access Framework #25

Open
opened 2026-09-05 16:20:13 +00:00 by dtourolle · 2 comments
Owner

FR-PLAT-AND-1 — library access on Android obtained exclusively through the Storage Access Framework. No SAF code exists.

The state of it

No ACTION_OPEN_DOCUMENT_TREE, no takePersistableUriPermission, no DocumentsContract. Android reaches a library through a Nextcloud account or a folder, over paths, like the desktop.

This requirement previously carried two tags and both have been removed, because what they rested on was intent rather than code:

  • A SourceRef::Document variant constructed only inside #[cfg(test)]. LocalStorage::open refuses it, and the test proving so is named a_reference_of_the_wrong_kind_is_refused_rather_than_guessed_at.
  • dr_plat::imports_supported, which returns false on Android, and whose own documentation says it "stops being false when a SAF implementation lands". That tag documented the absence of the thing it was counted as evidence for.

CONTRIBUTING.md and code-health.md CH-4 both warn about exactly this case: plumbing a future feature would use, counted as the feature.

Why it is the keystone of the Android cluster

It blocks:

  • #26 — FR-PLAT-AND-2 cannot detect the loss of a granted tree permission until there is a permission to lose.
  • Spike S10 — whether SAF at 10k files meets NFR-P1/P3 — which has no code to measure.

And §4.8 observes that publishing on Play is what turns SAF from a preference into a constraint (see #31).

Acceptance

  • A tree grant obtained through ACTION_OPEN_DOCUMENT_TREE and persisted.
  • SourceRef::Document is resolvable outside tests; LocalStorage::open honours it.
  • dr_plat::imports_supported returns true on Android, and its documentation stops describing a future.
  • Measured at 10k files (spike S10) before it is claimed.

See docs/outstanding.md §5.

**FR-PLAT-AND-1 — library access on Android obtained exclusively through the Storage Access Framework.** No SAF code exists. ## The state of it No `ACTION_OPEN_DOCUMENT_TREE`, no `takePersistableUriPermission`, no `DocumentsContract`. Android reaches a library through a Nextcloud account or a folder, **over paths, like the desktop**. This requirement previously carried two tags and both have been removed, because what they rested on was intent rather than code: - A `SourceRef::Document` variant constructed only inside `#[cfg(test)]`. `LocalStorage::open` refuses it, and the test proving so is named `a_reference_of_the_wrong_kind_is_refused_rather_than_guessed_at`. - `dr_plat::imports_supported`, which **returns false on Android**, and whose own documentation says it "stops being false when a SAF implementation lands". That tag documented the absence of the thing it was counted as evidence for. `CONTRIBUTING.md` and `code-health.md` CH-4 both warn about exactly this case: plumbing a future feature would use, counted as the feature. ## Why it is the keystone of the Android cluster It blocks: - #26 — FR-PLAT-AND-2 cannot detect the loss of a granted tree permission until there is a permission to lose. - Spike S10 — whether SAF at 10k files meets NFR-P1/P3 — which has no code to measure. And §4.8 observes that publishing on Play is what turns SAF from a preference into a constraint (see #31). ## Acceptance - [ ] A tree grant obtained through `ACTION_OPEN_DOCUMENT_TREE` and persisted. - [ ] `SourceRef::Document` is resolvable outside tests; `LocalStorage::open` honours it. - [ ] `dr_plat::imports_supported` returns true on Android, and its documentation stops describing a future. - [ ] Measured at 10k files (spike S10) before it is claimed. See `docs/outstanding.md` §5.
dtourolle added the unmet-requirementsize:Landroid labels 2026-09-05 16:20:13 +00:00
Author
Owner

Blocks #26 (no permission to lose) and spike S10 (no SAF code to measure).
Design alongside #29 — the portal picker on Linux is the same problem with a different API, and LocalStorage::grant is where both land.
Informed by #31 — whether Play makes this binding.

**Blocks** #26 (no permission to lose) and spike S10 (no SAF code to measure). **Design alongside** #29 — the portal picker on Linux is the same problem with a different API, and `LocalStorage::grant` is where both land. **Informed by** #31 — whether Play makes this binding.
Author
Owner

caae65c7, in v0.19.3, makes dr_plat::imports_supported true on Android, but not through SAF. So this issue stays open, and one of its premises has changed.

An import reads the camera card by path (/storage/9C33-6BBD) with "all files access" (MANAGE_EXTERNAL_STORAGE), not through a tree grant. Cards.java lists the volumes through StorageManager. Until the user grants access, the import page asks for it and opens the system settings page. SAF was not used for two reasons:

  • Since API 30, ACTION_OPEN_DOCUMENT_TREE refuses the root of an SD card.
  • Reading through SAF would need a second storage implementation under the importer, where reading by path needs none.

What this means for the acceptance list above:

  • dr_plat::imports_supported returns true on Android. Its documentation now describes the path route instead of a future SAF implementation.
  • Still open: the tree grant, SourceRef::Document, and S10. The library still reaches its files through an account or a folder, as before.

Two points need a decision:

  • The requirement's wording. FR-PLAT-AND-1 says "exclusively" through SAF. Either the requirement scopes that word to the library, or the card import has to move to SAF.
  • Play. MANAGE_EXTERNAL_STORAGE is one of the permissions Play restricts. That is fine while DarkRoom is sideloaded, but it changes what S11 (#31) would conclude if Play is ever a channel.
**caae65c7, in v0.19.3, makes `dr_plat::imports_supported` true on Android, but not through SAF.** So this issue stays open, and one of its premises has changed. An import reads the camera card by path (`/storage/9C33-6BBD`) with "all files access" (`MANAGE_EXTERNAL_STORAGE`), not through a tree grant. `Cards.java` lists the volumes through `StorageManager`. Until the user grants access, the import page asks for it and opens the system settings page. SAF was not used for two reasons: - Since API 30, `ACTION_OPEN_DOCUMENT_TREE` refuses the root of an SD card. - Reading through SAF would need a second storage implementation under the importer, where reading by path needs none. What this means for the acceptance list above: - [x] `dr_plat::imports_supported` returns true on Android. Its documentation now describes the path route instead of a future SAF implementation. - [ ] Still open: the tree grant, `SourceRef::Document`, and S10. The *library* still reaches its files through an account or a folder, as before. Two points need a decision: - **The requirement's wording.** FR-PLAT-AND-1 says "exclusively" through SAF. Either the requirement scopes that word to the library, or the card import has to move to SAF. - **Play.** `MANAGE_EXTERNAL_STORAGE` is one of the permissions Play restricts. That is fine while DarkRoom is sideloaded, but it changes what S11 (#31) would conclude if Play is ever a channel.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dtourolle/DarkRoom#25