Merge "Parcel only the canonical Uri.Part representation, not both." into qt-qpr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7a9c03e529
@@ -1987,17 +1987,26 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
* Enum which indicates which representation of a given part we have.
|
* Enum which indicates which representation of a given part we have.
|
||||||
*/
|
*/
|
||||||
static class Representation {
|
static class Representation {
|
||||||
static final int BOTH = 0;
|
|
||||||
static final int ENCODED = 1;
|
static final int ENCODED = 1;
|
||||||
static final int DECODED = 2;
|
static final int 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 != NOT_CACHED) {
|
||||||
|
this.mCanonicalRepresentation = Representation.ENCODED;
|
||||||
this.encoded = encoded;
|
this.encoded = encoded;
|
||||||
|
this.decoded = NOT_CACHED;
|
||||||
|
} else if (decoded != NOT_CACHED) {
|
||||||
|
this.mCanonicalRepresentation = Representation.DECODED;
|
||||||
|
this.encoded = NOT_CACHED;
|
||||||
this.decoded = decoded;
|
this.decoded = decoded;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Neither encoded nor decoded");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract String getEncoded();
|
abstract String getEncoded();
|
||||||
@@ -2009,25 +2018,21 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final void writeTo(Parcel parcel) {
|
final void writeTo(Parcel parcel) {
|
||||||
@SuppressWarnings("StringEquality")
|
final String canonicalValue;
|
||||||
boolean hasEncoded = encoded != NOT_CACHED;
|
if (mCanonicalRepresentation == Representation.ENCODED) {
|
||||||
|
canonicalValue = encoded;
|
||||||
@SuppressWarnings("StringEquality")
|
} else if (mCanonicalRepresentation == Representation.DECODED) {
|
||||||
boolean hasDecoded = decoded != NOT_CACHED;
|
canonicalValue = decoded;
|
||||||
|
|
||||||
if (hasEncoded && hasDecoded) {
|
|
||||||
parcel.writeInt(Representation.BOTH);
|
|
||||||
parcel.writeString(encoded);
|
|
||||||
parcel.writeString(decoded);
|
|
||||||
} else if (hasEncoded) {
|
|
||||||
parcel.writeInt(Representation.ENCODED);
|
|
||||||
parcel.writeString(encoded);
|
|
||||||
} else if (hasDecoded) {
|
|
||||||
parcel.writeInt(Representation.DECODED);
|
|
||||||
parcel.writeString(decoded);
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Neither encoded nor decoded");
|
throw new IllegalArgumentException("Unknown representation: "
|
||||||
|
+ mCanonicalRepresentation);
|
||||||
}
|
}
|
||||||
|
if (canonicalValue == NOT_CACHED) {
|
||||||
|
throw new AssertionError("Canonical value not cached ("
|
||||||
|
+ mCanonicalRepresentation + ")");
|
||||||
|
}
|
||||||
|
parcel.writeInt(mCanonicalRepresentation);
|
||||||
|
parcel.writeString(canonicalValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2059,13 +2064,12 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
|
|
||||||
static Part readFrom(Parcel parcel) {
|
static Part readFrom(Parcel parcel) {
|
||||||
int representation = parcel.readInt();
|
int representation = parcel.readInt();
|
||||||
|
String value = parcel.readString();
|
||||||
switch (representation) {
|
switch (representation) {
|
||||||
case Representation.BOTH:
|
|
||||||
return from(parcel.readString(), parcel.readString());
|
|
||||||
case Representation.ENCODED:
|
case Representation.ENCODED:
|
||||||
return fromEncoded(parcel.readString());
|
return fromEncoded(value);
|
||||||
case Representation.DECODED:
|
case Representation.DECODED:
|
||||||
return fromDecoded(parcel.readString());
|
return fromDecoded(value);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown representation: "
|
throw new IllegalArgumentException("Unknown representation: "
|
||||||
+ representation);
|
+ representation);
|
||||||
@@ -2127,6 +2131,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
private static class EmptyPart extends Part {
|
private static class EmptyPart extends Part {
|
||||||
public EmptyPart(String value) {
|
public EmptyPart(String value) {
|
||||||
super(value, value);
|
super(value, value);
|
||||||
|
if (value != null && !value.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Expected empty value, got: " + value);
|
||||||
|
}
|
||||||
|
// Avoid having to re-calculate the non-canonical value.
|
||||||
|
encoded = decoded = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2245,14 +2254,12 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
|
|||||||
static PathPart readFrom(Parcel parcel) {
|
static PathPart readFrom(Parcel parcel) {
|
||||||
int representation = parcel.readInt();
|
int representation = parcel.readInt();
|
||||||
switch (representation) {
|
switch (representation) {
|
||||||
case Representation.BOTH:
|
|
||||||
return from(parcel.readString(), parcel.readString());
|
|
||||||
case Representation.ENCODED:
|
case Representation.ENCODED:
|
||||||
return fromEncoded(parcel.readString());
|
return fromEncoded(parcel.readString());
|
||||||
case Representation.DECODED:
|
case Representation.DECODED:
|
||||||
return fromDecoded(parcel.readString());
|
return fromDecoded(parcel.readString());
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Bad representation: " + representation);
|
throw new IllegalArgumentException("Unknown representation: " + representation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import androidx.test.filters.SmallTest;
|
|||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
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;
|
||||||
@@ -816,6 +819,63 @@ public class UriTest extends TestCase {
|
|||||||
Uri.parse("content://com.example/path%2Fpath")));
|
Uri.parse("content://com.example/path%2Fpath")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that calling Part(String, String) with inconsistent Strings does not lead
|
||||||
|
* to the Part's encoded vs. decoded values being inconsistent.
|
||||||
|
*/
|
||||||
|
public void testPart_consistentEncodedVsDecoded() throws Exception {
|
||||||
|
Object authority = createPart(Class.forName("android.net.Uri$Part"), "a.com", "b.com");
|
||||||
|
Object path = createPart(Class.forName("android.net.Uri$PathPart"), "/foo/a", "/foo/b");
|
||||||
|
Uri uri = makeHierarchicalUri(authority, path);
|
||||||
|
// In these cases, decoding/encoding the encoded/decoded representation yields the same
|
||||||
|
// String, so we can just assert equality.
|
||||||
|
// assertEquals(uri.getPath(), uri.getEncodedPath());
|
||||||
|
assertEquals(uri.getAuthority(), uri.getEncodedAuthority());
|
||||||
|
|
||||||
|
// When both encoded and decoded strings are given, the encoded one is preferred.
|
||||||
|
assertEquals("a.com", uri.getAuthority());
|
||||||
|
assertEquals("/foo/a", uri.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object createPart(Class partClass, String encoded, String decoded) throws Exception {
|
||||||
|
Constructor partConstructor = partClass.getDeclaredConstructor(String.class, String.class);
|
||||||
|
partConstructor.setAccessible(true);
|
||||||
|
return partConstructor.newInstance(encoded, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Uri makeHierarchicalUri(Object authority, Object path) throws Exception {
|
||||||
|
Class hierarchicalUriClass = Class.forName("android.net.Uri$HierarchicalUri");
|
||||||
|
Constructor hierarchicalUriConstructor = hierarchicalUriClass.getDeclaredConstructors()[0];
|
||||||
|
hierarchicalUriConstructor.setAccessible(true);
|
||||||
|
return (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Attempting to unparcel a legacy parcel format of Uri.{,Path}Part should fail. */
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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