diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fb6bf0ab7c..23d34af5d2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -124,6 +124,8 @@ * Text: * SSA: Add support for UTF-16 files if they start with a byte order mark ([#319](https://github.com/androidx/media/issues/319)). +* Test Utilities: + * Check for URI scheme case insensitivity in `DataSourceContractTest`. * Remove deprecated symbols: * Remove `DefaultAudioSink` constructors, use `DefaultAudioSink.Builder` instead. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java index 78d8793d2a..a6ef7e82df 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/ContentDataSource.java @@ -75,13 +75,13 @@ public final class ContentDataSource extends BaseDataSource { @SuppressWarnings("InlinedApi") // We are inlining EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT. public long open(DataSpec dataSpec) throws ContentDataSourceException { try { - Uri uri = dataSpec.uri; + Uri uri = dataSpec.uri.normalizeScheme(); this.uri = uri; transferInitializing(dataSpec); AssetFileDescriptor assetFileDescriptor; - if ("content".equals(dataSpec.uri.getScheme())) { + if ("content".equals(uri.getScheme())) { Bundle providerOptions = new Bundle(); // We don't want compatible media transcoding. providerOptions.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true); diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSchemeDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSchemeDataSource.java index 859c5ad66b..3a35603222 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSchemeDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSchemeDataSource.java @@ -50,7 +50,7 @@ public final class DataSchemeDataSource extends BaseDataSource { public long open(DataSpec dataSpec) throws IOException { transferInitializing(dataSpec); this.dataSpec = dataSpec; - Uri uri = dataSpec.uri; + Uri uri = dataSpec.uri.normalizeScheme(); String scheme = uri.getScheme(); Assertions.checkArgument(SCHEME_DATA.equals(scheme), "Unsupported scheme: " + scheme); String[] uriParts = Util.split(uri.getSchemeSpecificPart(), ","); 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 b406cd8303..f7090d2120 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/RawResourceDataSource.java @@ -116,7 +116,7 @@ public final class RawResourceDataSource extends BaseDataSource { @Override public long open(DataSpec dataSpec) throws RawResourceDataSourceException { - Uri uri = dataSpec.uri; + Uri uri = dataSpec.uri.normalizeScheme(); this.uri = uri; int resourceId; @@ -150,10 +150,13 @@ public final class RawResourceDataSource extends BaseDataSource { } } else { throw new RawResourceDataSourceException( - "URI must either use scheme " + "Unsupported URI scheme (" + + uri.getScheme() + + "). Only " + RAW_RESOURCE_SCHEME - + " or " - + ContentResolver.SCHEME_ANDROID_RESOURCE, + + " and " + + ContentResolver.SCHEME_ANDROID_RESOURCE + + " are supported.", /* cause= */ null, PlaybackException.ERROR_CODE_FAILED_RUNTIME_CHECK); } diff --git a/libraries/datasource/src/test/java/androidx/media3/datasource/ResolvingDataSourceContractTest.java b/libraries/datasource/src/test/java/androidx/media3/datasource/ResolvingDataSourceContractTest.java index 520af06dd2..228c792b84 100644 --- a/libraries/datasource/src/test/java/androidx/media3/datasource/ResolvingDataSourceContractTest.java +++ b/libraries/datasource/src/test/java/androidx/media3/datasource/ResolvingDataSourceContractTest.java @@ -68,7 +68,7 @@ public class ResolvingDataSourceContractTest extends DataSourceContractTest { new Resolver() { @Override public DataSpec resolveDataSpec(DataSpec dataSpec) throws IOException { - return URI.equals(dataSpec.uri.toString()) + return URI.equals(dataSpec.uri.normalizeScheme().toString()) ? dataSpec.buildUpon().setUri(RESOLVED_URI).build() : dataSpec; } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DataSourceContractTest.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DataSourceContractTest.java index 74fa27a0f3..bc483fdfe9 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/DataSourceContractTest.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/DataSourceContractTest.java @@ -386,6 +386,44 @@ public abstract class DataSourceContractTest { } } + @Test + public void uriSchemeIsCaseInsensitive() throws Exception { + ImmutableList resources = getTestResources(); + Assertions.checkArgument(!resources.isEmpty(), "Must provide at least one test resource."); + + for (int i = 0; i < resources.size(); i++) { + additionalFailureInfo.setInfo(getFailureLabel(resources, i)); + TestResource resource = resources.get(i); + @Nullable String scheme = resource.getUri().getScheme(); + if (scheme == null) { + // No scheme for which to check case-insensitivity. + continue; + } + DataSource dataSource = createDataSource(); + Uri uri = + resource + .getUri() + .buildUpon() + .scheme(invertAsciiCaseOfEveryOtherCharacter(scheme)) + .build(); + try { + long length = dataSource.open(new DataSpec.Builder().setUri(uri).build()); + byte[] data = + unboundedReadsAreIndefinite() + ? DataSourceUtil.readExactly(dataSource, resource.getExpectedBytes().length) + : DataSourceUtil.readToEnd(dataSource); + + if (length != C.LENGTH_UNSET) { + assertThat(length).isEqualTo(resource.getExpectedBytes().length); + } + assertThat(data).isEqualTo(resource.getExpectedBytes()); + } finally { + dataSource.close(); + } + additionalFailureInfo.setInfo(null); + } + } + @Test public void resourceNotFound() throws Exception { DataSource dataSource = createDataSource(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeDataSet.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeDataSet.java index 7d9e156d34..12ca43eee5 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeDataSet.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeDataSet.java @@ -267,7 +267,7 @@ public class FakeDataSet { /** Returns a new {@link FakeData} with the given {@code uri}. */ public FakeData newData(Uri uri) { FakeData data = new FakeData(this, uri); - dataMap.put(uri, data); + dataMap.put(uri.normalizeScheme(), data); return data; } @@ -280,7 +280,7 @@ public class FakeDataSet { /** Returns the data for the given {@code uri}, or {@code defaultData} if no data is set. */ @Nullable public FakeData getData(Uri uri) { - @Nullable FakeData data = dataMap.get(uri); + @Nullable FakeData data = dataMap.get(uri.normalizeScheme()); return data != null ? data : defaultData; } diff --git a/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeDataSetTest.java b/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeDataSetTest.java index e924729ccc..65d2206c8c 100644 --- a/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeDataSetTest.java +++ b/libraries/test_utils/src/test/java/androidx/media3/test/utils/FakeDataSetTest.java @@ -51,11 +51,11 @@ public final class FakeDataSetTest { .setData(uris[2], testData[3]); assertThat(fakeDataSet.getAllData().size()).isEqualTo(4); - assertThat(fakeDataSet.getData("unseen_uri")).isEqualTo(fakeDataSet.getData((Uri) null)); + assertThat(fakeDataSet.getData("unseen_uri")).isEqualTo(fakeDataSet.getData("not a real key")); for (int i = 0; i < 3; i++) { assertThat(fakeDataSet.getData(uris[i]).uri).isEqualTo(uris[i]); } - assertThat(fakeDataSet.getData((Uri) null).getData()).isEqualTo(testData[0]); + assertThat(fakeDataSet.getData("not a real key").getData()).isEqualTo(testData[0]); for (int i = 1; i < 4; i++) { assertThat(fakeDataSet.getData(uris[i - 1]).getData()).isEqualTo(testData[i]); } @@ -79,7 +79,7 @@ public final class FakeDataSetTest { .appendReadError(exception) .endData(); - List segments = fakeDataSet.getData((Uri) null).getSegments(); + List segments = fakeDataSet.getData("not a real key").getSegments(); assertThat(segments.size()).isEqualTo(5); assertSegment(segments.get(0), testData, 3, 0, null, null); assertSegment(segments.get(1), testData, 3, 3, null, null); @@ -90,7 +90,15 @@ public final class FakeDataSetTest { byte[] allData = new byte[6]; System.arraycopy(testData, 0, allData, 0, 3); System.arraycopy(testData, 0, allData, 3, 3); - assertThat(fakeDataSet.getData((Uri) null).getData()).isEqualTo(allData); + assertThat(fakeDataSet.getData("not a real key").getData()).isEqualTo(allData); + } + + @Test + public void uriSchemesAreCaseInsensitive() { + byte[] data = TestUtil.buildTestData(3); + FakeDataSet fakeDataSet = new FakeDataSet().setData("HtTp://example.test/path/to/data", data); + + assertThat(fakeDataSet.getData("hTtP://example.test/path/to/data").getData()).isEqualTo(data); } private static void assertSegment(