v1.0.75: exclude Android app-private trees (Android/data,media,obb)
Build & Release APK / build (push) Successful in 12m49s

Scoped storage (Android 11+) lets a SAF grant LIST another app's
Android/media|data|obb dir but not OPEN the files, so every transfer there
failed and the pair was stuck reporting Partial forever (65 failures under
Android/media/com.whatsapp/ on Zahra). These hold app-managed data, not user
content, so exclude the whole subtree on both sides via path-prefix match in
isExcludedPath. ExcludePathTest covers the new prefixes + case-insensitivity.
This commit is contained in:
2026-06-07 13:01:05 +00:00
parent fb26e83484
commit ddb558263f
3 changed files with 31 additions and 3 deletions
@@ -57,6 +57,22 @@ class ExcludePathTest {
assertTrue(isExcludedPath("a/b/.photo.jpg.sfpart", p))
}
@Test fun `android app-private trees are excluded (scoped-storage unreadable)`() {
val p = pair()
assertTrue(isExcludedPath("Android/media/com.whatsapp/WhatsApp/Media/IMG.jpg", p))
assertTrue(isExcludedPath("Android/data/com.foo/files/x.bin", p))
assertTrue(isExcludedPath("Android/obb/com.game/main.obb", p))
assertTrue(isExcludedPath("android/MEDIA/com.x/y.jpg", p)) // case-insensitive
}
@Test fun `non-private Android paths are not excluded`() {
val p = pair()
// A user folder literally named "Android" at a deeper level is fine; only the
// top-level app-private trees are blocked.
assertFalse(isExcludedPath("DCIM/Android/holiday.jpg", p))
assertFalse(isExcludedPath("Pictures/android-wallpaper.png", p))
}
@Test fun `normal media files are not excluded`() {
val p = pair()
assertFalse(isExcludedPath("DCIM/Camera/IMG_0001.jpg", p))