Tag the SAF export path FR-EXP-10 only, not FR-PLAT-AND-1

saf.rs and the export path's SAF branch shipped tagged FR-PLAT-AND-1,
and the matrix counted the requirement as covered. Its subject is the
library — reached through SAF grants — and Android still reaches a
library over a Nextcloud account or a folder path. What the SAF code
does is give an album a folder on the tablet, which is FR-EXP-10.

outstanding.md said the figure overstated it and should be read with
this one subtracted; it now says the tags were narrowed, and coverage
reads 161 of 192.
This commit is contained in:
2026-09-26 16:19:55 -04:00
parent 7a56d16df1
commit 94ea2569ee
3 changed files with 12 additions and 8 deletions
+6 -6
View File
@@ -250,12 +250,12 @@ about.
0.17.0 brought the first real SAF code, for albums (FR-EXP-10): `FolderPicker.java` starts 0.17.0 brought the first real SAF code, for albums (FR-EXP-10): `FolderPicker.java` starts
`ACTION_OPEN_DOCUMENT_TREE` from a translucent activity of its own (the main activity is `ACTION_OPEN_DOCUMENT_TREE` from a translucent activity of its own (the main activity is
`NativeActivity`, whose results are not ours) and takes a persistable grant; `Saf.java` writes each `NativeActivity`, whose results are not ours) and takes a persistable grant; `Saf.java` writes each
export through `DocumentsContract`; `ui/dr-ui/src/saf.rs` is the JNI bridge. `saf.rs` and the export export through `DocumentsContract`; `ui/dr-ui/src/saf.rs` is the JNI bridge. They shipped tagged
path now carry `TRACES: FR-PLAT-AND-1`, and the matrix counts the requirement as covered. **That `TRACES: FR-PLAT-AND-1`, which made the matrix count the requirement as covered, and that overstated
overstates it.** The mechanism is the one the requirement names, but its subject is the library, and it: the mechanism is the one the requirement names, but its subject is the library, and Android
Android still reaches a library through a Nextcloud account or a folder, over paths, like the still reaches a library through a Nextcloud account or a folder, over paths, like the desktop. The
desktop. Either the tags narrow to FR-EXP-10 or the requirement is met for the library too; until tags now say FR-EXP-10 alone (0.17.1), so FR-PLAT-AND-1 reads as uncovered again until the library
one of those, read the coverage figure with this one subtracted. itself is reached through SAF.
That has a consequence for the rest of the cluster: **FR-PLAT-AND-2** — detecting the loss of a That has a consequence for the rest of the cluster: **FR-PLAT-AND-2** — detecting the loss of a
granted tree permission and marking images offline rather than deleting rows — is still blocked for granted tree permission and marking images offline rather than deleting rows — is still blocked for
+1 -1
View File
@@ -213,7 +213,7 @@ pub fn place(
if destination.trim().is_empty() { if destination.trim().is_empty() {
return Err("No export folder is set. Choose an album to export to.".into()); return Err("No export folder is set. Choose an album to export to.".into());
} }
// TRACES: FR-EXP-10 | FR-PLAT-AND-1 // TRACES: FR-EXP-10
// A SAF tree on Android: written through the provider, which may // A SAF tree on Android: written through the provider, which may
// rename on a collision, so the name it reports is the one kept. // rename on a collision, so the name it reports is the one kept.
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
+5 -1
View File
@@ -1,6 +1,10 @@
//! TRACES: FR-EXP-10 | FR-PLAT-AND-1 //! TRACES: FR-EXP-10
//! Android's Storage Access Framework, for an album's folder on the device. //! Android's Storage Access Framework, for an album's folder on the device.
//! //!
//! Export folders only. FR-PLAT-AND-1 asks for the *library* to be reached
//! through SAF, and it still is not — a library on the tablet is a server —
//! so this module does not claim it.
//!
//! The folder is chosen in the system's own picker, which can make a new //! The folder is chosen in the system's own picker, which can make a new
//! folder too, and comes back as a tree URI with a persisted grant. Exports //! folder too, and comes back as a tree URI with a persisted grant. Exports
//! are then written into it through `DocumentsContract` — a tree URI is not a //! are then written into it through `DocumentsContract` — a tree URI is not a