Joining files picked through the system picker failed outright whenever the strategy was stream copy -- the matched-files case the UI advertises as "joined without re-encoding, no quality loss". [ffkitsaf @ ...] Protocol 'ffkitsaf' not on whitelist 'file,crypto,data'! Error opening input file .../joined_from_content.concat_list.txt JoinScreen picks with OpenMultipleDocuments, so real inputs are always content://. ConcatEngine maps each through getSafParameterForRead and FFmpegConcatCommand writes the resulting ffkitsaf: paths into the concat list file. The demuxer applies its own protocol whitelist, defaulting to file,crypto,data, and -safe 0 does not touch it: that permits absolute paths, this permits the scheme they carry. Two separate gates, and only one was open. Nothing caught it because the two halves of the bug never met. Only STREAM_COPY feeds the demuxer a list file -- REENCODE passes each input with its own -i, where the whitelist does not apply -- so joining over SAF worked for mismatched clips. And every join test passed Uri.fromFile, which takes ConcatEngine's uri.path arm instead of the bridge, so matchingClipsAreJoinedByStreamCopy exercised stream copy with a file: path and passed. The one broken combination was the one no test produced and the only one a user can reach. That is #225's gap: FFmpegKitConfig.getSafParameterForRead is on every real conversion and join, and was on no passing test -- only on UnopenableUriTest's failure side, which proves the error message rather than the bridge. ContentUriInputTest now drives both the convert and join paths from a real content:// URI. It uses a plain ContentProvider, because the documents provider cannot be reached. Measured three ways: a DOCUMENTS_PROVIDER without MANAGE_DOCUMENTS is refused at install, instrumentation runs in the target app's process so Instrumentation.getContext() still carries the app's uid and is denied, and adoptShellPermissionIdentity(MANAGE_DOCUMENTS) is denied identically -- the denial naming ACTION_OPEN_DOCUMENT as the only way in. The bridge needs no documents provider: it opens a descriptor through the resolver, so any readable content:// URI exercises it, and an ordinary provider may be exported unprotected. The whole class stays headless. Recorded on #226, which that also settles: its cheap half does not exist. Verified on a local API 34 emulator: 66 tests, 0 failures, 3 skipped; and removing the -protocol_whitelist pair reproduces the production failure verbatim in the join test and nothing else. FFmpegConcatCommandTest pins the flag on the JVM. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
70 lines
3.9 KiB
XML
70 lines
3.9 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!--
|
|
The first manifest this source set has ever had, and it exists for one component.
|
|
|
|
SafPickerRoundTripTest drives the real system file picker. DocumentsUI only shows what a
|
|
DocumentsProvider offers it, so a test that picks a file needs a provider to pick from, and
|
|
that provider has to be declared: a ContentProvider is instantiated by the system from a
|
|
manifest entry and cannot be registered from test code.
|
|
|
|
It is declared HERE rather than in src/debug on purpose. src/debug would put a fake storage
|
|
root inside the shipped debug APK, where it would show up in every developer's own file
|
|
picker and in every other app's; this way it is installed only by the instrumentation APK,
|
|
alongside the test that needs it, and is gone the moment that APK is uninstalled.
|
|
|
|
The four attributes are not decoration. Each one is required for the picker to see it:
|
|
|
|
exported DocumentsUI is another app; an unexported provider is invisible to it.
|
|
permission MANAGE_DOCUMENTS is held by DocumentsUI and essentially nothing else,
|
|
so this is what stops any installed app from reading the fixture. The
|
|
provider is exported to the *picker*, not to the world.
|
|
grantUriPermissions How the app under test ends up able to read the URI it was handed. The
|
|
picker returns the document URI with FLAG_GRANT_READ_URI_PERMISSION,
|
|
and that flag does nothing unless the provider allows grants. Without
|
|
it the pick "succeeds" and every read of the result fails.
|
|
DOCUMENTS_PROVIDER The action DocumentsUI queries the package manager for. No filter, no
|
|
root in the drawer.
|
|
|
|
The authority carries the .test suffix because this component belongs to the instrumentation
|
|
package (org.libremediaconverter.test), not to the app. Authorities are global to the device:
|
|
reusing the app's would collide with the app on any device where both are installed.
|
|
-->
|
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
|
|
<application>
|
|
<provider
|
|
android:name="org.libremediaconverter.saf.FixtureDocumentsProvider"
|
|
android:authorities="org.libremediaconverter.test.fixtures"
|
|
android:exported="true"
|
|
android:grantUriPermissions="true"
|
|
android:permission="android.permission.MANAGE_DOCUMENTS">
|
|
<intent-filter>
|
|
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
|
</intent-filter>
|
|
</provider>
|
|
|
|
<!--
|
|
A PLAIN provider, for the ffkitsaf bridge on the success path.
|
|
|
|
FFmpegKitConfig.getSafParameterForRead is on every real user conversion and was on no
|
|
passing test: they all pass Uri.fromFile, which takes the other arm. Only its failure
|
|
side was covered, by UnopenableUriTest naming an authority that does not exist.
|
|
|
|
The documents provider above cannot serve this. Any DOCUMENTS_PROVIDER must hold
|
|
MANAGE_DOCUMENTS or the platform refuses to install it, instrumentation runs in the
|
|
target app's process and so carries the app's uid, and the resulting denial says what
|
|
is actually required: access obtained through ACTION_OPEN_DOCUMENT. That means a picker,
|
|
and the flake it brings. See issue #226.
|
|
|
|
The bridge does not need a documents provider. It opens a descriptor through the
|
|
resolver and hands FFmpeg a saf: path, so any readable content:// URI exercises it, and
|
|
an ordinary provider is allowed to be exported without a permission.
|
|
-->
|
|
<provider
|
|
android:name="org.libremediaconverter.saf.FixtureContentProvider"
|
|
android:authorities="org.libremediaconverter.test.content"
|
|
android:exported="true" />
|
|
</application>
|
|
|
|
</manifest>
|