Merge changes from topic "callz"

* changes:
  Expand ContentProviderOperation to support call().
  Add nullability annotations for current behavior.
This commit is contained in:
Jeff Sharkey
2019-08-09 16:01:23 +00:00
committed by Android (Google) Code Review
7 changed files with 817 additions and 575 deletions

View File

@@ -9480,47 +9480,62 @@ package android.content {
}
public class ContentProviderOperation implements android.os.Parcelable {
method public android.content.ContentProviderResult apply(android.content.ContentProvider, android.content.ContentProviderResult[], int) throws android.content.OperationApplicationException;
method @NonNull public android.content.ContentProviderResult apply(@NonNull android.content.ContentProvider, @NonNull android.content.ContentProviderResult[], int) throws android.content.OperationApplicationException;
method public int describeContents();
method public android.net.Uri getUri();
method @NonNull public android.net.Uri getUri();
method public boolean isAssertQuery();
method public boolean isCall();
method public boolean isDelete();
method public boolean isExceptionAllowed();
method public boolean isInsert();
method public boolean isReadOperation();
method public boolean isUpdate();
method public boolean isWriteOperation();
method public boolean isYieldAllowed();
method public static android.content.ContentProviderOperation.Builder newAssertQuery(android.net.Uri);
method public static android.content.ContentProviderOperation.Builder newDelete(android.net.Uri);
method public static android.content.ContentProviderOperation.Builder newInsert(android.net.Uri);
method public static android.content.ContentProviderOperation.Builder newUpdate(android.net.Uri);
method public String[] resolveSelectionArgsBackReferences(android.content.ContentProviderResult[], int);
method public android.content.ContentValues resolveValueBackReferences(android.content.ContentProviderResult[], int);
method @NonNull public static android.content.ContentProviderOperation.Builder newAssertQuery(@NonNull android.net.Uri);
method @NonNull public static android.content.ContentProviderOperation.Builder newCall(@NonNull android.net.Uri, @Nullable String, @Nullable String);
method @NonNull public static android.content.ContentProviderOperation.Builder newDelete(@NonNull android.net.Uri);
method @NonNull public static android.content.ContentProviderOperation.Builder newInsert(@NonNull android.net.Uri);
method @NonNull public static android.content.ContentProviderOperation.Builder newUpdate(@NonNull android.net.Uri);
method @Nullable public android.os.Bundle resolveExtrasBackReferences(@NonNull android.content.ContentProviderResult[], int);
method @Nullable public String[] resolveSelectionArgsBackReferences(@NonNull android.content.ContentProviderResult[], int);
method @Nullable public android.content.ContentValues resolveValueBackReferences(@NonNull android.content.ContentProviderResult[], int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.ContentProviderOperation> CREATOR;
}
public static class ContentProviderOperation.Builder {
method public android.content.ContentProviderOperation build();
method public android.content.ContentProviderOperation.Builder withExpectedCount(int);
method public android.content.ContentProviderOperation.Builder withSelection(String, String[]);
method public android.content.ContentProviderOperation.Builder withSelectionBackReference(int, int);
method public android.content.ContentProviderOperation.Builder withValue(String, Object);
method public android.content.ContentProviderOperation.Builder withValueBackReference(String, int);
method public android.content.ContentProviderOperation.Builder withValueBackReferences(android.content.ContentValues);
method public android.content.ContentProviderOperation.Builder withValues(android.content.ContentValues);
method public android.content.ContentProviderOperation.Builder withYieldAllowed(boolean);
method @NonNull public android.content.ContentProviderOperation build();
method @NonNull public android.content.ContentProviderOperation.Builder withExceptionAllowed(boolean);
method @NonNull public android.content.ContentProviderOperation.Builder withExpectedCount(int);
method @NonNull public android.content.ContentProviderOperation.Builder withExtra(@NonNull String, @Nullable Object);
method @NonNull public android.content.ContentProviderOperation.Builder withExtraBackReference(@NonNull String, int);
method @NonNull public android.content.ContentProviderOperation.Builder withExtraBackReference(@NonNull String, int, @NonNull String);
method @NonNull public android.content.ContentProviderOperation.Builder withExtras(@NonNull android.os.Bundle);
method @NonNull public android.content.ContentProviderOperation.Builder withSelection(@Nullable String, @Nullable String[]);
method @NonNull public android.content.ContentProviderOperation.Builder withSelectionBackReference(int, int);
method @NonNull public android.content.ContentProviderOperation.Builder withSelectionBackReference(int, int, @NonNull String);
method @NonNull public android.content.ContentProviderOperation.Builder withValue(@NonNull String, @Nullable Object);
method @NonNull public android.content.ContentProviderOperation.Builder withValueBackReference(@NonNull String, int);
method @NonNull public android.content.ContentProviderOperation.Builder withValueBackReference(@NonNull String, int, @NonNull String);
method @NonNull public android.content.ContentProviderOperation.Builder withValueBackReferences(@NonNull android.content.ContentValues);
method @NonNull public android.content.ContentProviderOperation.Builder withValues(@NonNull android.content.ContentValues);
method @NonNull public android.content.ContentProviderOperation.Builder withYieldAllowed(boolean);
}
public class ContentProviderResult implements android.os.Parcelable {
ctor public ContentProviderResult(android.net.Uri);
ctor public ContentProviderResult(@NonNull android.net.Uri);
ctor public ContentProviderResult(int);
ctor public ContentProviderResult(@NonNull android.os.Bundle);
ctor public ContentProviderResult(@NonNull Exception);
ctor public ContentProviderResult(android.os.Parcel);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.ContentProviderResult> CREATOR;
field public final Integer count;
field public final android.net.Uri uri;
field @Nullable public final Integer count;
field @Nullable public final Exception exception;
field @Nullable public final android.os.Bundle extras;
field @Nullable public final android.net.Uri uri;
}
public class ContentQueryMap extends java.util.Observable {

File diff suppressed because it is too large Load Diff

View File

@@ -16,40 +16,50 @@
package android.content;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelableException;
import com.android.internal.util.Preconditions;
import java.util.Objects;
/**
* Contains the result of the application of a {@link ContentProviderOperation}. It is guaranteed
* to have exactly one of {@link #uri} or {@link #count} set.
* Contains the result of the application of a {@link ContentProviderOperation}.
* <p>
* It is guaranteed to have exactly one of {@link #uri}, {@link #count},
* {@link #extras}, or {@link #exception} set.
*/
public class ContentProviderResult implements Parcelable {
public final Uri uri;
public final Integer count;
/** {@hide} */
public final String failure;
public final @Nullable Uri uri;
public final @Nullable Integer count;
public final @Nullable Bundle extras;
public final @Nullable Exception exception;
public ContentProviderResult(Uri uri) {
this(Preconditions.checkNotNull(uri), null, null);
public ContentProviderResult(@NonNull Uri uri) {
this(Objects.requireNonNull(uri), null, null, null);
}
public ContentProviderResult(int count) {
this(null, count, null);
this(null, count, null, null);
}
public ContentProviderResult(@NonNull Bundle extras) {
this(null, null, Objects.requireNonNull(extras), null);
}
public ContentProviderResult(@NonNull Exception exception) {
this(null, null, null, exception);
}
/** {@hide} */
public ContentProviderResult(String failure) {
this(null, null, failure);
}
/** {@hide} */
public ContentProviderResult(Uri uri, Integer count, String failure) {
public ContentProviderResult(Uri uri, Integer count, Bundle extras, Exception exception) {
this.uri = uri;
this.count = count;
this.failure = failure;
this.extras = extras;
this.exception = exception;
}
public ContentProviderResult(Parcel source) {
@@ -64,9 +74,14 @@ public class ContentProviderResult implements Parcelable {
count = null;
}
if (source.readInt() != 0) {
failure = source.readString();
extras = source.readBundle();
} else {
failure = null;
extras = null;
}
if (source.readInt() != 0) {
exception = (Exception) ParcelableException.readFromParcel(source);
} else {
exception = null;
}
}
@@ -74,7 +89,8 @@ public class ContentProviderResult implements Parcelable {
public ContentProviderResult(ContentProviderResult cpr, int userId) {
uri = ContentProvider.maybeAddUserId(cpr.uri, userId);
count = cpr.count;
failure = cpr.failure;
extras = cpr.extras;
exception = cpr.exception;
}
@Override
@@ -91,9 +107,15 @@ public class ContentProviderResult implements Parcelable {
} else {
dest.writeInt(0);
}
if (failure != null) {
if (extras != null) {
dest.writeInt(1);
dest.writeString(failure);
dest.writeBundle(extras);
} else {
dest.writeInt(0);
}
if (exception != null) {
dest.writeInt(1);
ParcelableException.writeToParcel(dest, exception);
} else {
dest.writeInt(0);
}
@@ -126,8 +148,11 @@ public class ContentProviderResult implements Parcelable {
if (count != null) {
sb.append("count=" + count + " ");
}
if (uri != null) {
sb.append("failure=" + failure + " ");
if (extras != null) {
sb.append("extras=" + extras + " ");
}
if (exception != null) {
sb.append("exception=" + exception + " ");
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");

View File

@@ -16,6 +16,8 @@
package android.content;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -217,6 +219,33 @@ public final class ContentValues implements Parcelable {
mMap.put(key, null);
}
/** {@hide} */
public void putObject(@Nullable String key, @Nullable Object value) {
if (value == null) {
putNull(key);
} else if (value instanceof String) {
put(key, (String) value);
} else if (value instanceof Byte) {
put(key, (Byte) value);
} else if (value instanceof Short) {
put(key, (Short) value);
} else if (value instanceof Integer) {
put(key, (Integer) value);
} else if (value instanceof Long) {
put(key, (Long) value);
} else if (value instanceof Float) {
put(key, (Float) value);
} else if (value instanceof Double) {
put(key, (Double) value);
} else if (value instanceof Boolean) {
put(key, (Boolean) value);
} else if (value instanceof byte[]) {
put(key, (byte[]) value);
} else {
throw new IllegalArgumentException("Unsupported type " + value.getClass());
}
}
/**
* Returns the number of values.
*
@@ -556,4 +585,31 @@ public final class ContentValues implements Parcelable {
}
return sb.toString();
}
/** {@hide} */
public static boolean isSupportedValue(Object value) {
if (value == null) {
return true;
} else if (value instanceof String) {
return true;
} else if (value instanceof Byte) {
return true;
} else if (value instanceof Short) {
return true;
} else if (value instanceof Integer) {
return true;
} else if (value instanceof Long) {
return true;
} else if (value instanceof Float) {
return true;
} else if (value instanceof Double) {
return true;
} else if (value instanceof Boolean) {
return true;
} else if (value instanceof byte[]) {
return true;
} else {
return false;
}
}
}

View File

@@ -563,6 +563,35 @@ public class BaseBundle {
return mMap.keySet();
}
/** {@hide} */
public void putObject(@Nullable String key, @Nullable Object value) {
if (value == null) {
putString(key, null);
} else if (value instanceof Boolean) {
putBoolean(key, (Boolean) value);
} else if (value instanceof Integer) {
putInt(key, (Integer) value);
} else if (value instanceof Long) {
putLong(key, (Long) value);
} else if (value instanceof Double) {
putDouble(key, (Double) value);
} else if (value instanceof String) {
putString(key, (String) value);
} else if (value instanceof boolean[]) {
putBooleanArray(key, (boolean[]) value);
} else if (value instanceof int[]) {
putIntArray(key, (int[]) value);
} else if (value instanceof long[]) {
putLongArray(key, (long[]) value);
} else if (value instanceof double[]) {
putDoubleArray(key, (double[]) value);
} else if (value instanceof String[]) {
putStringArray(key, (String[]) value);
} else {
throw new IllegalArgumentException("Unsupported type " + value.getClass());
}
}
/**
* Inserts a Boolean value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.

View File

@@ -435,6 +435,56 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
return bundle;
}
/** {@hide} */
@Override
public void putObject(@Nullable String key, @Nullable Object value) {
if (value instanceof Byte) {
putByte(key, (Byte) value);
} else if (value instanceof Character) {
putChar(key, (Character) value);
} else if (value instanceof Short) {
putShort(key, (Short) value);
} else if (value instanceof Float) {
putFloat(key, (Float) value);
} else if (value instanceof CharSequence) {
putCharSequence(key, (CharSequence) value);
} else if (value instanceof Parcelable) {
putParcelable(key, (Parcelable) value);
} else if (value instanceof Size) {
putSize(key, (Size) value);
} else if (value instanceof SizeF) {
putSizeF(key, (SizeF) value);
} else if (value instanceof Parcelable[]) {
putParcelableArray(key, (Parcelable[]) value);
} else if (value instanceof ArrayList) {
putParcelableArrayList(key, (ArrayList) value);
} else if (value instanceof List) {
putParcelableList(key, (List) value);
} else if (value instanceof SparseArray) {
putSparseParcelableArray(key, (SparseArray) value);
} else if (value instanceof Serializable) {
putSerializable(key, (Serializable) value);
} else if (value instanceof byte[]) {
putByteArray(key, (byte[]) value);
} else if (value instanceof short[]) {
putShortArray(key, (short[]) value);
} else if (value instanceof char[]) {
putCharArray(key, (char[]) value);
} else if (value instanceof float[]) {
putFloatArray(key, (float[]) value);
} else if (value instanceof CharSequence[]) {
putCharSequenceArray(key, (CharSequence[]) value);
} else if (value instanceof Bundle) {
putBundle(key, (Bundle) value);
} else if (value instanceof Binder) {
putBinder(key, (Binder) value);
} else if (value instanceof IBinder) {
putIBinder(key, (IBinder) value);
} else {
super.putObject(key, value);
}
}
/**
* Inserts a byte value into the mapping of this Bundle, replacing
* any existing value for the given key.

View File

@@ -26,11 +26,6 @@ import androidx.test.filters.SmallTest;
import junit.framework.TestCase;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -39,11 +34,6 @@ public class ContentProviderOperationTest extends TestCase {
private final static Uri sTestUri1 = Uri.parse("content://authority/blah");
private final static ContentValues sTestValues1;
private final static Class<ContentProviderOperation.Builder> CLASS_BUILDER =
ContentProviderOperation.Builder.class;
private final static Class<ContentProviderOperation> CLASS_OPERATION =
ContentProviderOperation.class;
static {
sTestValues1 = new ContentValues();
sTestValues1.put("a", 1);
@@ -279,221 +269,6 @@ public class ContentProviderOperationTest extends TestCase {
assertEquals("a,103,101,b,102", TextUtils.join(",", s2));
}
public void testParcelingOperation() throws NoSuchFieldException, IllegalAccessException,
NoSuchMethodException, InvocationTargetException, InstantiationException {
Parcel parcel = Parcel.obtain();
ContentProviderOperation op1;
ContentProviderOperation op2;
HashMap<Integer, Integer> selArgsBackRef = new HashMap<Integer, Integer>();
selArgsBackRef.put(1, 2);
selArgsBackRef.put(3, 4);
ContentValues values = new ContentValues();
values.put("v1", "val1");
values.put("v2", "43");
ContentValues valuesBackRef = new ContentValues();
values.put("v3", "val3");
values.put("v4", "44");
try {
ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(
Uri.parse("content://goo/bar"));
builderSetExpectedCount(builder, 42);
builderSetSelection(builder, "selection");
builderSetSelectionArgs(builder, new String[]{"a", "b"});
builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
builderSetValues(builder, values);
builderSetValuesBackReferences(builder, valuesBackRef);
op1 = newOperationFromBuilder(builder);
op1.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
assertEquals(ContentProviderOperation.TYPE_INSERT, operationGetType(op2));
assertEquals("content://goo/bar", operationGetUri(op2).toString());
assertEquals(Integer.valueOf(42), operationGetExpectedCount(op2));
assertEquals("selection", operationGetSelection(op2));
assertEquals(2, operationGetSelectionArgs(op2).length);
assertEquals("a", operationGetSelectionArgs(op2)[0]);
assertEquals("b", operationGetSelectionArgs(op2)[1]);
assertEquals(values, operationGetValues(op2));
assertEquals(valuesBackRef, operationGetValuesBackReferences(op2));
assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
} finally {
parcel.recycle();
}
try {
ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(
Uri.parse("content://goo/bar"));
builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
op1 = newOperationFromBuilder(builder);
op1.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
assertEquals(ContentProviderOperation.TYPE_UPDATE, operationGetType(op2));
assertEquals("content://goo/bar", operationGetUri(op2).toString());
assertNull(operationGetExpectedCount(op2));
assertNull(operationGetSelection(op2));
assertNull(operationGetSelectionArgs(op2));
assertNull(operationGetValues(op2));
assertNull(operationGetValuesBackReferences(op2));
assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
} finally {
parcel.recycle();
}
try {
ContentProviderOperation.Builder builder = ContentProviderOperation.newDelete(
Uri.parse("content://goo/bar"));
op1 = newOperationFromBuilder(builder);
op1.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
assertEquals(ContentProviderOperation.TYPE_DELETE, operationGetType(op2));
assertEquals("content://goo/bar", operationGetUri(op2).toString());
assertNull(operationGetExpectedCount(op2));
assertNull(operationGetSelection(op2));
assertNull(operationGetSelectionArgs(op2));
assertNull(operationGetValues(op2));
assertNull(operationGetValuesBackReferences(op2));
assertNull(operationGetSelectionArgsBackReferences(op2));
} finally {
parcel.recycle();
}
}
private static ContentProviderOperation newOperationFromBuilder(
ContentProviderOperation.Builder builder)
throws NoSuchMethodException, InstantiationException, IllegalAccessException,
InvocationTargetException {
final Constructor constructor = CLASS_OPERATION.getDeclaredConstructor(CLASS_BUILDER);
constructor.setAccessible(true);
return (ContentProviderOperation) constructor.newInstance(builder);
}
private void builderSetSelectionArgsBackReferences(
ContentProviderOperation.Builder builder, HashMap<Integer, Integer> selArgsBackRef)
throws NoSuchFieldException, IllegalAccessException {
Field field;
field = CLASS_BUILDER.getDeclaredField("mSelectionArgsBackReferences");
field.setAccessible(true);
field.set(builder, selArgsBackRef);
}
private void builderSetValuesBackReferences(
ContentProviderOperation.Builder builder, ContentValues valuesBackReferences)
throws NoSuchFieldException, IllegalAccessException {
Field field;
field = CLASS_BUILDER.getDeclaredField("mValuesBackReferences");
field.setAccessible(true);
field.set(builder, valuesBackReferences);
}
private void builderSetSelection(
ContentProviderOperation.Builder builder, String selection)
throws NoSuchFieldException, IllegalAccessException {
Field field;
field = CLASS_BUILDER.getDeclaredField("mSelection");
field.setAccessible(true);
field.set(builder, selection);
}
private void builderSetSelectionArgs(
ContentProviderOperation.Builder builder, String[] selArgs)
throws NoSuchFieldException, IllegalAccessException {
Field field;
field = CLASS_BUILDER.getDeclaredField("mSelectionArgs");
field.setAccessible(true);
field.set(builder, selArgs);
}
private void builderSetValues(
ContentProviderOperation.Builder builder, ContentValues values)
throws NoSuchFieldException, IllegalAccessException {
Field field;
field = CLASS_BUILDER.getDeclaredField("mValues");
field.setAccessible(true);
field.set(builder, values);
}
private void builderSetExpectedCount(
ContentProviderOperation.Builder builder, Integer expectedCount)
throws NoSuchFieldException, IllegalAccessException {
Field field;
field = CLASS_BUILDER.getDeclaredField("mExpectedCount");
field.setAccessible(true);
field.set(builder, expectedCount);
}
private int operationGetType(ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mType");
field.setAccessible(true);
return field.getInt(operation);
}
private Uri operationGetUri(ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mUri");
field.setAccessible(true);
return (Uri) field.get(operation);
}
private String operationGetSelection(ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mSelection");
field.setAccessible(true);
return (String) field.get(operation);
}
private String[] operationGetSelectionArgs(ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgs");
field.setAccessible(true);
return (String[]) field.get(operation);
}
private ContentValues operationGetValues(ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mValues");
field.setAccessible(true);
return (ContentValues) field.get(operation);
}
private Integer operationGetExpectedCount(ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mExpectedCount");
field.setAccessible(true);
return (Integer) field.get(operation);
}
private ContentValues operationGetValuesBackReferences(ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mValuesBackReferences");
field.setAccessible(true);
return (ContentValues) field.get(operation);
}
private Map<Integer, Integer> operationGetSelectionArgsBackReferences(
ContentProviderOperation operation)
throws NoSuchFieldException, IllegalAccessException {
final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgsBackReferences");
field.setAccessible(true);
return (Map<Integer, Integer>) field.get(operation);
}
public void testParcelingResult() {
Parcel parcel = Parcel.obtain();
ContentProviderResult result1;