From 3bbb0e74536572e303126cac2da5b7174f7d20c0 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 19 Feb 2024 02:25:35 -0800 Subject: [PATCH] Add documented support for `android.resource://package/id` URIs These were previously somewhat supported, but the `package` part was never read (so it only worked if it was either absent or the same as the current application's package). This change parses and uses the `package` part. This partially reverts https://github.com/androidx/media/commit/35121a2d3dda1658c2baea5dc4ff3b2f6821e34d. This is hard to test for the same reasons as https://github.com/androidx/media/commit/88f554c74b7df9122914fb976cd3e6eb00976212: > This is hard to test because to do so robustly requires being able to guaranteed that another test APK will be installed with a known raw resource inside it. PiperOrigin-RevId: 608270463 --- RELEASENOTES.md | 5 ++ .../RawResourceDataSourceContractTest.java | 9 +- .../datasource/RawResourceDataSource.java | 87 +++++++++++-------- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6de9b18558..c3a55538c6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -18,6 +18,11 @@ * Metadata: * Image: * DRM: +* DataSource: + * Implement support for `android.resource://package/id` raw resource URIs + where `package` is different to the package of the current application. + This wasn't previously documented to work, but is a more efficient way + of accessing resources in another package than by name. * Effect: * Improved PQ to SDR tone-mapping by converting color spaces. * Support multiple speed changes within the same `EditedMediaItem` or diff --git a/libraries/datasource/src/androidTest/java/androidx/media3/datasource/RawResourceDataSourceContractTest.java b/libraries/datasource/src/androidTest/java/androidx/media3/datasource/RawResourceDataSourceContractTest.java index 6080fbb935..e128285ae0 100644 --- a/libraries/datasource/src/androidTest/java/androidx/media3/datasource/RawResourceDataSourceContractTest.java +++ b/libraries/datasource/src/androidTest/java/androidx/media3/datasource/RawResourceDataSourceContractTest.java @@ -93,8 +93,13 @@ public final class RawResourceDataSourceContractTest extends DataSourceContractT .setExpectedBytes(RESOURCE_1_DATA) .build(), new TestResource.Builder() - .setName("android.resource:// with (unused) package and ID") - .setUri(Uri.parse("android.resource://unused.package.name/" + R.raw.resource1)) + .setName("android.resource:// with package and ID") + .setUri( + Uri.parse( + "android.resource://" + + ApplicationProvider.getApplicationContext().getPackageName() + + "/" + + R.raw.resource1)) .setExpectedBytes(RESOURCE_1_DATA) .build()); } diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java index 6d5da27b8f..87522f1591 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java @@ -36,6 +36,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.channels.FileChannel; +import java.util.List; /** * A {@link DataSource} for reading a raw resource. @@ -43,28 +44,29 @@ import java.nio.channels.FileChannel; *

URIs supported by this source are: * *

* - *

URIs of the form {@code android.resource://package/id} are also supported, although the - * package part is not needed and is not used. This support is due to this format being prevalent in - * the ecosystem (including being recommended on Stack - * Overflow). + *

If {@code package} is specified in either of the above URI forms, it must be visible to the current + * application. * - *

{@code new - * Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE).path(Integer.toString(resourceId)).build()} - * can be used to build supported {@link Uri}s. + *

Supported {@link Uri} instances can be built as follows: + * + *

{@code
+ * Uri.Builder()
+ *     .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ *     .authority(packageName)
+ *     .path(Integer.toString(resourceId))
+ *     .build();
+ * }
*/ @UnstableApi public final class RawResourceDataSource extends BaseDataSource { @@ -209,18 +211,16 @@ public final class RawResourceDataSource extends BaseDataSource { Uri normalizedUri = dataSpec.uri.normalizeScheme(); Resources resources; int resourceId; - if (TextUtils.equals(RAW_RESOURCE_SCHEME, normalizedUri.getScheme()) - || (TextUtils.equals(ContentResolver.SCHEME_ANDROID_RESOURCE, normalizedUri.getScheme()) - && normalizedUri.getPathSegments().size() == 1 - && Assertions.checkNotNull(normalizedUri.getLastPathSegment()).matches("\\d+"))) { + if (TextUtils.equals(RAW_RESOURCE_SCHEME, normalizedUri.getScheme())) { resources = applicationContext.getResources(); - try { - resourceId = Integer.parseInt(Assertions.checkNotNull(normalizedUri.getLastPathSegment())); - } catch (NumberFormatException e) { + List pathSegments = normalizedUri.getPathSegments(); + if (pathSegments.size() == 1) { + resourceId = parseResourceId(pathSegments.get(0)); + } else { throw new RawResourceDataSourceException( - "Resource identifier must be an integer.", - /* cause= */ null, - PlaybackException.ERROR_CODE_FAILED_RUNTIME_CHECK); + RAW_RESOURCE_SCHEME + + ":// URI must have exactly one path element, found " + + pathSegments.size()); } } else if (TextUtils.equals( ContentResolver.SCHEME_ANDROID_RESOURCE, normalizedUri.getScheme())) { @@ -247,18 +247,22 @@ public final class RawResourceDataSource extends BaseDataSource { PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND); } } - // The javadoc of this class already discourages the URI form that requires this API call. - @SuppressLint("DiscouragedApi") - int resourceIdFromName = - resources.getIdentifier( - packageName + ":" + path, /* defType= */ "raw", /* defPackage= */ null); - if (resourceIdFromName != 0) { - resourceId = resourceIdFromName; + if (path.matches("\\d+")) { + resourceId = parseResourceId(path); } else { - throw new RawResourceDataSourceException( - "Resource not found.", - /* cause= */ null, - PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND); + // The javadoc of this class already discourages the URI form that requires this API call. + @SuppressLint("DiscouragedApi") + int resourceIdFromName = + resources.getIdentifier( + packageName + ":" + path, /* defType= */ "raw", /* defPackage= */ null); + if (resourceIdFromName != 0) { + resourceId = resourceIdFromName; + } else { + throw new RawResourceDataSourceException( + "Resource not found.", + /* cause= */ null, + PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND); + } } } else { throw new RawResourceDataSourceException( @@ -288,6 +292,17 @@ public final class RawResourceDataSource extends BaseDataSource { return assetFileDescriptor; } + private static int parseResourceId(String resourceId) throws RawResourceDataSourceException { + try { + return Integer.parseInt(resourceId); + } catch (NumberFormatException e) { + throw new RawResourceDataSourceException( + "Resource identifier must be an integer.", + /* cause= */ null, + PlaybackException.ERROR_CODE_FAILED_RUNTIME_CHECK); + } + } + @Override public int read(byte[] buffer, int offset, int length) throws RawResourceDataSourceException { if (length == 0) {