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

Commit 295ddbae authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Allow failures of applyBatch() operations.

In some cases, the supplier of ContentProviderOperation is okay
if certain operations fail, and they'd like ContentProviderResult
to tell them about the failures instead of aborting the remainder
of the transaction.

Start using this for ModernMediaScanner, where we probably raced
with someone when building an UPSERT-style operation.  We'll
pick up any changes to those files during the next scan.

Bug: 128494336
Test: atest --test-mapping packages/providers/MediaProvider
Change-Id: Ida8230ff2bbb3bab56eb83928e49e7097bfbc9fd
parent eb24d06f
Loading
Loading
Loading
Loading
+29 −4
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package android.content;

import android.annotation.UnsupportedAppUsage;
import android.content.ContentProvider;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;
@@ -59,6 +58,7 @@ public class ContentProviderOperation implements Parcelable {
    private final ContentValues mValuesBackReferences;
    private final Map<Integer, Integer> mSelectionArgsBackReferences;
    private final boolean mYieldAllowed;
    private final boolean mFailureAllowed;

    private final static String TAG = "ContentProviderOperation";

@@ -76,6 +76,7 @@ public class ContentProviderOperation implements Parcelable {
        mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
        mValuesBackReferences = builder.mValuesBackReferences;
        mYieldAllowed = builder.mYieldAllowed;
        mFailureAllowed = builder.mFailureAllowed;
    }

    private ContentProviderOperation(Parcel source) {
@@ -98,6 +99,7 @@ public class ContentProviderOperation implements Parcelable {
            }
        }
        mYieldAllowed = source.readInt() != 0;
        mFailureAllowed = source.readInt() != 0;
    }

    /** @hide */
@@ -111,6 +113,7 @@ public class ContentProviderOperation implements Parcelable {
        mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences;
        mValuesBackReferences = cpo.mValuesBackReferences;
        mYieldAllowed = cpo.mYieldAllowed;
        mFailureAllowed = cpo.mFailureAllowed;
    }

    public void writeToParcel(Parcel dest, int flags) {
@@ -157,6 +160,7 @@ public class ContentProviderOperation implements Parcelable {
            dest.writeInt(0);
        }
        dest.writeInt(mYieldAllowed ? 1 : 0);
        dest.writeInt(mFailureAllowed ? 1 : 0);
    }

    /**
@@ -212,6 +216,11 @@ public class ContentProviderOperation implements Parcelable {
        return mYieldAllowed;
    }

    /** {@hide} */
    public boolean isFailureAllowed() {
        return mFailureAllowed;
    }

    /** @hide exposed for unit tests */
    @UnsupportedAppUsage
    public int getType() {
@@ -274,6 +283,14 @@ public class ContentProviderOperation implements Parcelable {
        return mType == TYPE_ASSERT;
    }

    private ContentProviderResult fail(String msg) throws OperationApplicationException {
        if (mFailureAllowed) {
            return new ContentProviderResult(msg);
        } else {
            throw new OperationApplicationException(msg);
        }
    }

    /**
     * Applies this operation using the given provider. The backRefs array is used to resolve any
     * back references that were requested using
@@ -297,7 +314,8 @@ public class ContentProviderOperation implements Parcelable {
        if (mType == TYPE_INSERT) {
            Uri newUri = provider.insert(mUri, values);
            if (newUri == null) {
                throw new OperationApplicationException("insert failed");
                Log.e(TAG, this.toString());
                return fail("Insert into " + mUri + " returned no result");
            }
            return new ContentProviderResult(newUri);
        }
@@ -329,7 +347,7 @@ public class ContentProviderOperation implements Parcelable {
                            if (!TextUtils.equals(cursorValue, expectedValue)) {
                                // Throw exception when expected values don't match
                                Log.e(TAG, this.toString());
                                throw new OperationApplicationException("Found value " + cursorValue
                                return fail("Found value " + cursorValue
                                        + " when expected " + expectedValue + " for column "
                                        + projection[i]);
                            }
@@ -346,7 +364,7 @@ public class ContentProviderOperation implements Parcelable {

        if (mExpectedCount != null && mExpectedCount != numRows) {
            Log.e(TAG, this.toString());
            throw new OperationApplicationException("wrong number of rows: " + numRows);
            return fail("Expected " + mExpectedCount + " rows but actual " + numRows);
        }

        return new ContentProviderResult(numRows);
@@ -491,6 +509,7 @@ public class ContentProviderOperation implements Parcelable {
        private ContentValues mValuesBackReferences;
        private Map<Integer, Integer> mSelectionArgsBackReferences;
        private boolean mYieldAllowed;
        private boolean mFailureAllowed;

        /** Create a {@link Builder} of a given type. The uri must not be null. */
        private Builder(int type, Uri uri) {
@@ -683,5 +702,11 @@ public class ContentProviderOperation implements Parcelable {
            mYieldAllowed = yieldAllowed;
            return this;
        }

        /** {@hide} */
        public Builder withFailureAllowed(boolean failureAllowed) {
            mFailureAllowed = failureAllowed;
            return this;
        }
    }
}
+60 −15
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@

package android.content;

import android.content.ContentProvider;
import android.net.Uri;
import android.os.Parcelable;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.util.Preconditions;

/**
 * Contains the result of the application of a {@link ContentProviderOperation}. It is guaranteed
@@ -28,26 +29,44 @@ import android.os.Parcel;
public class ContentProviderResult implements Parcelable {
    public final Uri uri;
    public final Integer count;
    /** {@hide} */
    public final String failure;

    public ContentProviderResult(Uri uri) {
        if (uri == null) throw new IllegalArgumentException("uri must not be null");
        this.uri = uri;
        this.count = null;
        this(Preconditions.checkNotNull(uri), null, null);
    }

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

    /** {@hide} */
    public ContentProviderResult(String failure) {
        this(null, null, failure);
    }

    /** {@hide} */
    public ContentProviderResult(Uri uri, Integer count, String failure) {
        this.uri = uri;
        this.count = count;
        this.uri = null;
        this.failure = failure;
    }

    public ContentProviderResult(Parcel source) {
        int type = source.readInt();
        if (type == 1) {
            count = source.readInt();
        if (source.readInt() != 0) {
            uri = Uri.CREATOR.createFromParcel(source);
        } else {
            uri = null;
        }
        if (source.readInt() != 0) {
            count = source.readInt();
        } else {
            count = null;
            uri = Uri.CREATOR.createFromParcel(source);
        }
        if (source.readInt() != 0) {
            failure = source.readString();
        } else {
            failure = null;
        }
    }

@@ -55,37 +74,63 @@ public class ContentProviderResult implements Parcelable {
    public ContentProviderResult(ContentProviderResult cpr, int userId) {
        uri = ContentProvider.maybeAddUserId(cpr.uri, userId);
        count = cpr.count;
        failure = cpr.failure;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        if (uri == null) {
        if (uri != null) {
            dest.writeInt(1);
            uri.writeToParcel(dest, flags);
        } else {
            dest.writeInt(0);
        }
        if (count != null) {
            dest.writeInt(1);
            dest.writeInt(count);
        } else {
            dest.writeInt(2);
            uri.writeToParcel(dest, 0);
            dest.writeInt(0);
        }
        if (failure != null) {
            dest.writeInt(1);
            dest.writeString(failure);
        } else {
            dest.writeInt(0);
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final @android.annotation.NonNull Creator<ContentProviderResult> CREATOR =
            new Creator<ContentProviderResult>() {
        @Override
        public ContentProviderResult createFromParcel(Parcel source) {
            return new ContentProviderResult(source);
        }

        @Override
        public ContentProviderResult[] newArray(int size) {
            return new ContentProviderResult[size];
        }
    };

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("ContentProviderResult(");
        if (uri != null) {
            sb.append("uri=" + uri + " ");
        }
        if (count != null) {
            sb.append("count=" + count + " ");
        }
        if (uri != null) {
            return "ContentProviderResult(uri=" + uri.toString() + ")";
            sb.append("failure=" + failure + " ");
        }
        return "ContentProviderResult(count=" + count + ")";
        sb.deleteCharAt(sb.length() - 1);
        sb.append(")");
        return sb.toString();
    }
}