Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d6fd4b8 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Expand ContentProviderOperation to support call().

ContentProviderOperation has long supported basic operations like
insert(), update(), and delete(), but it was never extended to
support the general-purpose call() method.

This change adds support for call(), including configuration of the
extras Bundle using back-references to other operations.  In
addition, the output Bundle from call() can be used for
back-references from other operations.

Clean up how back-references are handled internally; we now store
either a literal value in each slot, or an explicit BackReference
instance which we can "instanceof" check to resolve when needed.

Also add withExceptionAllowed() capability, which can be used to
catch and report the failure of a single operation while allowing
any subsequent operations to proceed.  Adds various nullability
annotations to reflect the behavior of all existing logic.

Bug: 131598520
Test: atest android.content.cts.ContentProviderOperationTest
Test: atest android.content.cts.ContentProviderResultTest
Change-Id: I1744bf8fc1ad048aa96460d487c2867c4c81d7b3
parent 6304d7b8
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -9482,16 +9482,20 @@ package android.content {
    method public int describeContents();
    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 @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);
@@ -9500,11 +9504,18 @@ package android.content {
  public static class ContentProviderOperation.Builder {
    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);
@@ -9513,11 +9524,15 @@ package android.content {
  public class ContentProviderResult implements android.os.Parcelable {
    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 @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;
  }
+556 −269

File changed.

Preview size limit exceeded, changes collapsed.

+42 −19
Original line number Diff line number Diff line
@@ -19,39 +19,47 @@ 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 @Nullable Uri uri;
    public final @Nullable Integer count;
    /** {@hide} */
    public final @Nullable String failure;
    public final @Nullable Bundle extras;
    public final @Nullable Exception exception;

    public ContentProviderResult(@NonNull Uri uri) {
        this(Preconditions.checkNotNull(uri), null, null);
        this(Objects.requireNonNull(uri), null, null, null);
    }

    public ContentProviderResult(int count) {
        this(null, count, null);
        this(null, count, null, null);
    }

    /** {@hide} */
    public ContentProviderResult(@NonNull String failure) {
        this(null, null, failure);
    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(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) {
@@ -66,9 +74,14 @@ public class ContentProviderResult implements Parcelable {
            count = null;
        }
        if (source.readInt() != 0) {
            failure = source.readString();
            extras = source.readBundle();
        } else {
            extras = null;
        }
        if (source.readInt() != 0) {
            exception = (Exception) ParcelableException.readFromParcel(source);
        } else {
            failure = null;
            exception = null;
        }
    }

@@ -76,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
@@ -93,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);
        }
@@ -128,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(")");
+56 −0
Original line number Diff line number Diff line
@@ -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;
        }
    }
}
+29 −0
Original line number Diff line number Diff line
@@ -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.
Loading