Add cross-package support to RawResourceDataSource
This has been documented to work since this class was created, but until now we were always trying to resolve using the current application's `Resources.getIdentifier` method. This commit changes to resolve to the other app's `Resources` object if the package name doesn't match the current package. This will only work if the current app has package-visibility to the destination package: http://g.co/dev/packagevisibility 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: 577864992
This commit is contained in:
parent
26789b56c2
commit
88f554c74b
@ -3,6 +3,10 @@
|
||||
### Unreleased changes
|
||||
|
||||
* Common Library:
|
||||
* Implement support for `android.resource://package/[type/]name` raw
|
||||
resource URIs where `package` is different to the package of the current
|
||||
application. This has always been documented to work, but wasn't
|
||||
correctly implemented until now.
|
||||
* ExoPlayer:
|
||||
* Add `PreloadMediaSource` and `PreloadMediaPeriod` that allows apps to
|
||||
preload the media source at a specific start position before playback,
|
||||
|
@ -18,8 +18,10 @@ package androidx.media3.datasource;
|
||||
import static androidx.media3.common.util.Util.castNonNull;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
@ -36,19 +38,25 @@ import java.io.InputStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* A {@link DataSource} for reading a raw resource inside the APK.
|
||||
* A {@link DataSource} for reading a raw resource.
|
||||
*
|
||||
* <p>URIs supported by this source are of one of the forms:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code rawresource:///id}, where {@code id} is the integer identifier of a raw resource.
|
||||
* <li>{@code rawresource:///id}, where {@code id} is the integer identifier of a raw resource in
|
||||
* this application.
|
||||
* <li>{@code android.resource:///id}, where {@code id} is the integer identifier of a raw
|
||||
* resource.
|
||||
* resource in this application.
|
||||
* <li>{@code android.resource://[package]/[type/]name}, where {@code package} is the name of the
|
||||
* package in which the resource is located, {@code type} is the resource type and {@code
|
||||
* name} is the resource name. The package and the type are optional. Their default value is
|
||||
* the package of this application and "raw", respectively. Using the two other forms is more
|
||||
* efficient.
|
||||
* <ul>
|
||||
* <li>If {@code package} is specified, it must be <a
|
||||
* href="https://developer.android.com/training/package-visibility">visible</a> to the
|
||||
* current application.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* <p>URIs of the form {@code android.resource://package/id} are also supported, although the
|
||||
@ -101,8 +109,7 @@ public final class RawResourceDataSource extends BaseDataSource {
|
||||
/** The scheme part of a raw resource URI. */
|
||||
public static final String RAW_RESOURCE_SCHEME = "rawresource";
|
||||
|
||||
private final Resources resources;
|
||||
private final String packageName;
|
||||
private final Context applicationContext;
|
||||
|
||||
@Nullable private Uri uri;
|
||||
@Nullable private AssetFileDescriptor assetFileDescriptor;
|
||||
@ -115,8 +122,7 @@ public final class RawResourceDataSource extends BaseDataSource {
|
||||
*/
|
||||
public RawResourceDataSource(Context context) {
|
||||
super(/* isNetwork= */ false);
|
||||
this.resources = context.getResources();
|
||||
this.packageName = context.getPackageName();
|
||||
this.applicationContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -124,11 +130,13 @@ public final class RawResourceDataSource extends BaseDataSource {
|
||||
Uri uri = dataSpec.uri.normalizeScheme();
|
||||
this.uri = uri;
|
||||
|
||||
Resources resources;
|
||||
int resourceId;
|
||||
if (TextUtils.equals(RAW_RESOURCE_SCHEME, uri.getScheme())
|
||||
|| (TextUtils.equals(ContentResolver.SCHEME_ANDROID_RESOURCE, uri.getScheme())
|
||||
&& uri.getPathSegments().size() == 1
|
||||
&& Assertions.checkNotNull(uri.getLastPathSegment()).matches("\\d+"))) {
|
||||
resources = applicationContext.getResources();
|
||||
try {
|
||||
resourceId = Integer.parseInt(Assertions.checkNotNull(uri.getLastPathSegment()));
|
||||
} catch (NumberFormatException e) {
|
||||
@ -142,12 +150,31 @@ public final class RawResourceDataSource extends BaseDataSource {
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
@Nullable String host = uri.getHost();
|
||||
String resourceName = (TextUtils.isEmpty(host) ? "" : (host + ":")) + path;
|
||||
resourceId =
|
||||
String packageName =
|
||||
TextUtils.isEmpty(uri.getHost()) ? applicationContext.getPackageName() : uri.getHost();
|
||||
if (packageName.equals(applicationContext.getPackageName())) {
|
||||
resources = applicationContext.getResources();
|
||||
} else {
|
||||
try {
|
||||
resources =
|
||||
applicationContext.getPackageManager().getResourcesForApplication(packageName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RawResourceDataSourceException(
|
||||
"Package in "
|
||||
+ ContentResolver.SCHEME_ANDROID_RESOURCE
|
||||
+ ":// URI not found. Check http://g.co/dev/packagevisibility.",
|
||||
e,
|
||||
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(
|
||||
resourceName, /* defType= */ "raw", /* defPackage= */ packageName);
|
||||
if (resourceId == 0) {
|
||||
packageName + ":" + path, /* defType= */ "raw", /* defPackage= */ null);
|
||||
if (resourceIdFromName != 0) {
|
||||
resourceId = resourceIdFromName;
|
||||
} else {
|
||||
throw new RawResourceDataSourceException(
|
||||
"Resource not found.",
|
||||
/* cause= */ null,
|
||||
|
Loading…
x
Reference in New Issue
Block a user