Update parcling logic for Uris.
Implicitly convert all Uris to StringUris during parcel read/write. Also update and/or remove irrelevant methods and tests. Bug: 231476072 Test: atest UriTest Change-Id: Ic7688a00a07705301e5b06ee8783e801395e9f15
This commit is contained in:
@@ -882,10 +882,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Uri readFrom(Parcel parcel) {
|
static Uri readFrom(Parcel parcel) {
|
||||||
|
final StringUri stringUri = new StringUri(parcel.readString8());
|
||||||
return new OpaqueUri(
|
return new OpaqueUri(
|
||||||
parcel.readString8(),
|
stringUri.parseScheme(),
|
||||||
Part.readFrom(parcel),
|
stringUri.getSsp(),
|
||||||
Part.readFrom(parcel)
|
stringUri.getFragmentPart()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -895,9 +896,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
|
|
||||||
public void writeToParcel(Parcel parcel, int flags) {
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
parcel.writeInt(TYPE_ID);
|
parcel.writeInt(TYPE_ID);
|
||||||
parcel.writeString8(scheme);
|
parcel.writeString8(toString());
|
||||||
ssp.writeTo(parcel);
|
|
||||||
fragment.writeTo(parcel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHierarchical() {
|
public boolean isHierarchical() {
|
||||||
@@ -1196,22 +1195,25 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
Part query, Part fragment) {
|
Part query, Part fragment) {
|
||||||
this.scheme = scheme;
|
this.scheme = scheme;
|
||||||
this.authority = Part.nonNull(authority);
|
this.authority = Part.nonNull(authority);
|
||||||
this.path = path == null ? PathPart.NULL : path;
|
this.path = generatePath(path);
|
||||||
this.query = Part.nonNull(query);
|
this.query = Part.nonNull(query);
|
||||||
this.fragment = Part.nonNull(fragment);
|
this.fragment = Part.nonNull(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uri readFrom(Parcel parcel) {
|
private PathPart generatePath(PathPart originalPath) {
|
||||||
final String scheme = parcel.readString8();
|
|
||||||
final Part authority = Part.readFrom(parcel);
|
|
||||||
// In RFC3986 the path should be determined based on whether there is a scheme or
|
// In RFC3986 the path should be determined based on whether there is a scheme or
|
||||||
// authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3).
|
// authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3).
|
||||||
final boolean hasSchemeOrAuthority =
|
final boolean hasSchemeOrAuthority =
|
||||||
(scheme != null && scheme.length() > 0) || !authority.isEmpty();
|
(scheme != null && scheme.length() > 0) || !authority.isEmpty();
|
||||||
final PathPart path = PathPart.readFrom(hasSchemeOrAuthority, parcel);
|
final PathPart newPath = hasSchemeOrAuthority ? PathPart.makeAbsolute(originalPath)
|
||||||
final Part query = Part.readFrom(parcel);
|
: originalPath;
|
||||||
final Part fragment = Part.readFrom(parcel);
|
return newPath == null ? PathPart.NULL : newPath;
|
||||||
return new HierarchicalUri(scheme, authority, path, query, fragment);
|
}
|
||||||
|
|
||||||
|
static Uri readFrom(Parcel parcel) {
|
||||||
|
final StringUri stringUri = new StringUri(parcel.readString8());
|
||||||
|
return new HierarchicalUri(stringUri.getScheme(), stringUri.getAuthorityPart(),
|
||||||
|
stringUri.getPathPart(), stringUri.getQueryPart(), stringUri.getFragmentPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
@@ -1220,11 +1222,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
|
|
||||||
public void writeToParcel(Parcel parcel, int flags) {
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
parcel.writeInt(TYPE_ID);
|
parcel.writeInt(TYPE_ID);
|
||||||
parcel.writeString8(scheme);
|
parcel.writeString8(toString());
|
||||||
authority.writeTo(parcel);
|
|
||||||
path.writeTo(parcel);
|
|
||||||
query.writeTo(parcel);
|
|
||||||
fragment.writeTo(parcel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHierarchical() {
|
public boolean isHierarchical() {
|
||||||
@@ -1994,21 +1992,14 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
*/
|
*/
|
||||||
static abstract class AbstractPart {
|
static abstract class AbstractPart {
|
||||||
|
|
||||||
// Possible values of mCanonicalRepresentation.
|
|
||||||
static final int REPRESENTATION_ENCODED = 1;
|
|
||||||
static final int REPRESENTATION_DECODED = 2;
|
|
||||||
|
|
||||||
volatile String encoded;
|
volatile String encoded;
|
||||||
volatile String decoded;
|
volatile String decoded;
|
||||||
private final int mCanonicalRepresentation;
|
|
||||||
|
|
||||||
AbstractPart(String encoded, String decoded) {
|
AbstractPart(String encoded, String decoded) {
|
||||||
if (encoded != NotCachedHolder.NOT_CACHED) {
|
if (encoded != NotCachedHolder.NOT_CACHED) {
|
||||||
this.mCanonicalRepresentation = REPRESENTATION_ENCODED;
|
|
||||||
this.encoded = encoded;
|
this.encoded = encoded;
|
||||||
this.decoded = NotCachedHolder.NOT_CACHED;
|
this.decoded = NotCachedHolder.NOT_CACHED;
|
||||||
} else if (decoded != NotCachedHolder.NOT_CACHED) {
|
} else if (decoded != NotCachedHolder.NOT_CACHED) {
|
||||||
this.mCanonicalRepresentation = REPRESENTATION_DECODED;
|
|
||||||
this.encoded = NotCachedHolder.NOT_CACHED;
|
this.encoded = NotCachedHolder.NOT_CACHED;
|
||||||
this.decoded = decoded;
|
this.decoded = decoded;
|
||||||
} else {
|
} else {
|
||||||
@@ -2023,24 +2014,6 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
boolean hasDecoded = decoded != NotCachedHolder.NOT_CACHED;
|
boolean hasDecoded = decoded != NotCachedHolder.NOT_CACHED;
|
||||||
return hasDecoded ? decoded : (decoded = decode(encoded));
|
return hasDecoded ? decoded : (decoded = decode(encoded));
|
||||||
}
|
}
|
||||||
|
|
||||||
final void writeTo(Parcel parcel) {
|
|
||||||
final String canonicalValue;
|
|
||||||
if (mCanonicalRepresentation == REPRESENTATION_ENCODED) {
|
|
||||||
canonicalValue = encoded;
|
|
||||||
} else if (mCanonicalRepresentation == REPRESENTATION_DECODED) {
|
|
||||||
canonicalValue = decoded;
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Unknown representation: "
|
|
||||||
+ mCanonicalRepresentation);
|
|
||||||
}
|
|
||||||
if (canonicalValue == NotCachedHolder.NOT_CACHED) {
|
|
||||||
throw new AssertionError("Canonical value not cached ("
|
|
||||||
+ mCanonicalRepresentation + ")");
|
|
||||||
}
|
|
||||||
parcel.writeInt(mCanonicalRepresentation);
|
|
||||||
parcel.writeString8(canonicalValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2069,20 +2042,6 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
return hasEncoded ? encoded : (encoded = encode(decoded));
|
return hasEncoded ? encoded : (encoded = encode(decoded));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Part readFrom(Parcel parcel) {
|
|
||||||
int representation = parcel.readInt();
|
|
||||||
String value = parcel.readString8();
|
|
||||||
switch (representation) {
|
|
||||||
case REPRESENTATION_ENCODED:
|
|
||||||
return fromEncoded(value);
|
|
||||||
case REPRESENTATION_DECODED:
|
|
||||||
return fromDecoded(value);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown representation: "
|
|
||||||
+ representation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns given part or {@link #NULL} if the given part is null.
|
* Returns given part or {@link #NULL} if the given part is null.
|
||||||
*/
|
*/
|
||||||
@@ -2258,23 +2217,6 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
return appendEncodedSegment(oldPart, encoded);
|
return appendEncodedSegment(oldPart, encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PathPart readFrom(Parcel parcel) {
|
|
||||||
int representation = parcel.readInt();
|
|
||||||
switch (representation) {
|
|
||||||
case REPRESENTATION_ENCODED:
|
|
||||||
return fromEncoded(parcel.readString8());
|
|
||||||
case REPRESENTATION_DECODED:
|
|
||||||
return fromDecoded(parcel.readString8());
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown representation: " + representation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static PathPart readFrom(boolean hasSchemeOrAuthority, Parcel parcel) {
|
|
||||||
final PathPart path = readFrom(parcel);
|
|
||||||
return hasSchemeOrAuthority ? makeAbsolute(path) : path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a path from the encoded string.
|
* Creates a path from the encoded string.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ import junit.framework.TestCase;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -869,84 +867,90 @@ public class UriTest extends TestCase {
|
|||||||
return (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
|
return (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Attempting to unparcel a legacy parcel format of Uri.{,Path}Part should fail. */
|
private Uri buildUriFromParts(boolean argumentsEncoded,
|
||||||
public void testUnparcelLegacyPart_fails() throws Exception {
|
|
||||||
assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$Part"));
|
|
||||||
assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$PathPart"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void assertUnparcelLegacyPart_fails(Class partClass) throws Exception {
|
|
||||||
Parcel parcel = Parcel.obtain();
|
|
||||||
parcel.writeInt(0 /* BOTH */);
|
|
||||||
parcel.writeString("encoded");
|
|
||||||
parcel.writeString("decoded");
|
|
||||||
parcel.setDataPosition(0);
|
|
||||||
|
|
||||||
Method readFromMethod = partClass.getDeclaredMethod("readFrom", Parcel.class);
|
|
||||||
readFromMethod.setAccessible(true);
|
|
||||||
try {
|
|
||||||
readFromMethod.invoke(null, parcel);
|
|
||||||
fail();
|
|
||||||
} catch (InvocationTargetException expected) {
|
|
||||||
Throwable targetException = expected.getTargetException();
|
|
||||||
// Check that the exception was thrown for the correct reason.
|
|
||||||
assertEquals("Unknown representation: 0", targetException.getMessage());
|
|
||||||
} finally {
|
|
||||||
parcel.recycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Uri buildUriFromRawParcel(boolean argumentsEncoded,
|
|
||||||
String scheme,
|
String scheme,
|
||||||
String authority,
|
String authority,
|
||||||
String path,
|
String path,
|
||||||
String query,
|
String query,
|
||||||
String fragment) {
|
String fragment) {
|
||||||
// Representation value (from AbstractPart.REPRESENTATION_{ENCODED,DECODED}).
|
final Uri.Builder builder = new Uri.Builder();
|
||||||
final int representation = argumentsEncoded ? 1 : 2;
|
builder.scheme(scheme);
|
||||||
Parcel parcel = Parcel.obtain();
|
if (argumentsEncoded) {
|
||||||
try {
|
builder.encodedAuthority(authority);
|
||||||
parcel.writeInt(3); // hierarchical
|
builder.encodedPath(path);
|
||||||
parcel.writeString8(scheme);
|
builder.encodedQuery(query);
|
||||||
parcel.writeInt(representation);
|
builder.encodedFragment(fragment);
|
||||||
parcel.writeString8(authority);
|
} else {
|
||||||
parcel.writeInt(representation);
|
builder.authority(authority);
|
||||||
parcel.writeString8(path);
|
builder.path(path);
|
||||||
parcel.writeInt(representation);
|
builder.query(query);
|
||||||
parcel.writeString8(query);
|
builder.fragment(fragment);
|
||||||
parcel.writeInt(representation);
|
|
||||||
parcel.writeString8(fragment);
|
|
||||||
parcel.setDataPosition(0);
|
|
||||||
return Uri.CREATOR.createFromParcel(parcel);
|
|
||||||
} finally {
|
|
||||||
parcel.recycle();
|
|
||||||
}
|
}
|
||||||
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUnparcelMalformedPath() {
|
public void testUnparcelMalformedPath() {
|
||||||
// Regression tests for b/171966843.
|
// Regression tests for b/171966843.
|
||||||
|
|
||||||
// Test cases with arguments encoded (covering testing `scheme` * `authority` options).
|
// Test cases with arguments encoded (covering testing `scheme` * `authority` options).
|
||||||
Uri uri0 = buildUriFromRawParcel(true, "https", "google.com", "@evil.com", null, null);
|
Uri uri0 = buildUriFromParts(true, "https", "google.com", "@evil.com", null, null);
|
||||||
assertEquals("https://google.com/@evil.com", uri0.toString());
|
assertEquals("https://google.com/@evil.com", uri0.toString());
|
||||||
Uri uri1 = buildUriFromRawParcel(true, null, "google.com", "@evil.com", "name=spark", "x");
|
Uri uri1 = buildUriFromParts(true, null, "google.com", "@evil.com", "name=spark", "x");
|
||||||
assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString());
|
assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString());
|
||||||
Uri uri2 = buildUriFromRawParcel(true, "http:", null, "@evil.com", null, null);
|
Uri uri2 = buildUriFromParts(true, "http:", null, "@evil.com", null, null);
|
||||||
assertEquals("http::/@evil.com", uri2.toString());
|
assertEquals("http::/@evil.com", uri2.toString());
|
||||||
Uri uri3 = buildUriFromRawParcel(true, null, null, "@evil.com", null, null);
|
Uri uri3 = buildUriFromParts(true, null, null, "@evil.com", null, null);
|
||||||
assertEquals("@evil.com", uri3.toString());
|
assertEquals("@evil.com", uri3.toString());
|
||||||
|
|
||||||
// Test cases with arguments not encoded (covering testing `scheme` * `authority` options).
|
// Test cases with arguments not encoded (covering testing `scheme` * `authority` options).
|
||||||
Uri uriA = buildUriFromRawParcel(false, "https", "google.com", "@evil.com", null, null);
|
Uri uriA = buildUriFromParts(false, "https", "google.com", "@evil.com", null, null);
|
||||||
assertEquals("https://google.com/%40evil.com", uriA.toString());
|
assertEquals("https://google.com/%40evil.com", uriA.toString());
|
||||||
Uri uriB = buildUriFromRawParcel(false, null, "google.com", "@evil.com", null, null);
|
Uri uriB = buildUriFromParts(false, null, "google.com", "@evil.com", null, null);
|
||||||
assertEquals("//google.com/%40evil.com", uriB.toString());
|
assertEquals("//google.com/%40evil.com", uriB.toString());
|
||||||
Uri uriC = buildUriFromRawParcel(false, "http:", null, "@evil.com", null, null);
|
Uri uriC = buildUriFromParts(false, "http:", null, "@evil.com", null, null);
|
||||||
assertEquals("http::/%40evil.com", uriC.toString());
|
assertEquals("http::/%40evil.com", uriC.toString());
|
||||||
Uri uriD = buildUriFromRawParcel(false, null, null, "@evil.com", "name=spark", "y");
|
Uri uriD = buildUriFromParts(false, null, null, "@evil.com", "name=spark", "y");
|
||||||
assertEquals("%40evil.com?name%3Dspark#y", uriD.toString());
|
assertEquals("%40evil.com?name%3Dspark#y", uriD.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testParsedUriFromStringEquality() {
|
||||||
|
Uri uri = buildUriFromParts(
|
||||||
|
true, "https", "google.com", "@evil.com", null, null);
|
||||||
|
assertEquals(uri, Uri.parse(uri.toString()));
|
||||||
|
Uri uri2 = buildUriFromParts(
|
||||||
|
true, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null);
|
||||||
|
assertEquals(uri2, Uri.parse(uri2.toString()));
|
||||||
|
Uri uri3 = buildUriFromParts(
|
||||||
|
false, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null);
|
||||||
|
assertEquals(uri3, Uri.parse(uri3.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParceledUrisAreEqual() {
|
||||||
|
Uri opaqueUri = Uri.fromParts("fake://uri#", "ssp", "fragment");
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
try {
|
||||||
|
opaqueUri.writeToParcel(parcel, 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel);
|
||||||
|
Uri parsedUri = Uri.parse(postParcelUri.toString());
|
||||||
|
assertEquals(parsedUri.getScheme(), postParcelUri.getScheme());
|
||||||
|
} finally {
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri hierarchicalUri = new Uri.Builder().scheme("fake://uri#").authority("auth").build();
|
||||||
|
parcel = Parcel.obtain();
|
||||||
|
try {
|
||||||
|
hierarchicalUri.writeToParcel(parcel, 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel);
|
||||||
|
Uri parsedUri = Uri.parse(postParcelUri.toString());
|
||||||
|
assertEquals(parsedUri.getScheme(), postParcelUri.getScheme());
|
||||||
|
} finally {
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testToSafeString() {
|
public void testToSafeString() {
|
||||||
checkToSafeString("tel:xxxxxx", "tel:Google");
|
checkToSafeString("tel:xxxxxx", "tel:Google");
|
||||||
checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
|
checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
|
||||||
|
|||||||
Reference in New Issue
Block a user