diff options
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/content/ContentProvider.java | 38 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderClient.java | 16 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderNative.java | 98 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderOperation.java | 146 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderResult.java | 40 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 100 | ||||
-rw-r--r-- | core/java/android/content/IContentProvider.java | 11 | ||||
-rw-r--r-- | core/java/android/content/IEntityIterator.java | 11 | ||||
-rw-r--r-- | core/java/android/database/DatabaseUtils.java | 15 |
9 files changed, 360 insertions, 115 deletions
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index c204dda..bb25b68 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -151,9 +151,23 @@ public abstract class ContentProvider implements ComponentCallbacks { return ContentProvider.this.bulkInsert(uri, initialValues); } - public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) { + public Uri insertEntity(Uri uri, Entity entities) { checkWritePermission(uri); - return ContentProvider.this.bulkInsertEntities(uri, entities); + return ContentProvider.this.insertEntity(uri, entities); + } + + public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + throws OperationApplicationException { + for (ContentProviderOperation operation : operations) { + if (operation.isReadOperation()) { + checkReadPermission(operation.getUri()); + } + + if (operation.isWriteOperation()) { + checkWritePermission(operation.getUri()); + } + } + return ContentProvider.this.applyBatch(operations); } public int delete(Uri uri, String selection, String[] selectionArgs) { @@ -167,9 +181,9 @@ public abstract class ContentProvider implements ComponentCallbacks { return ContentProvider.this.update(uri, values, selection, selectionArgs); } - public int[] bulkUpdateEntities(Uri uri, Entity[] entities) { + public int updateEntity(Uri uri, Entity entity) { checkWritePermission(uri); - return ContentProvider.this.bulkUpdateEntities(uri, entities); + return ContentProvider.this.updateEntity(uri, entity); } public ParcelFileDescriptor openFile(Uri uri, String mode) @@ -403,14 +417,6 @@ public abstract class ContentProvider implements ComponentCallbacks { throw new UnsupportedOperationException(); } - public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) { - Uri[] result = new Uri[entities.length]; - for (int i = 0; i < entities.length; i++) { - result[i] = insertEntity(uri, entities[i]); - } - return result; - } - /** * A request to delete one or more rows. The selection clause is applied when performing * the deletion, allowing the operation to affect multiple rows in a @@ -459,14 +465,6 @@ public abstract class ContentProvider implements ComponentCallbacks { throw new UnsupportedOperationException(); } - public int[] bulkUpdateEntities(Uri uri, Entity[] entities) { - int[] result = new int[entities.length]; - for (int i = 0; i < entities.length; i++) { - result[i] = updateEntity(uri, entities[i]); - } - return result; - } - /** * Open a file blob associated with a content URI. * This method can be called from multiple diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index ed3ed42..08d4fca 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -96,22 +96,18 @@ public class ContentProviderClient { /** see {@link ContentProvider#insertEntity} */ public Uri insertEntity(Uri uri, Entity entity) throws RemoteException { - return mContentProvider.bulkInsertEntities(uri, new Entity[]{entity})[0]; - } - - /** see {@link ContentProvider#bulkInsertEntities} */ - public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) throws RemoteException { - return mContentProvider.bulkInsertEntities(uri, entities); + return mContentProvider.insertEntity(uri, entity); } /** see {@link ContentProvider#updateEntity} */ public int updateEntity(Uri uri, Entity entity) throws RemoteException { - return mContentProvider.bulkUpdateEntities(uri, new Entity[]{entity})[0]; + return mContentProvider.updateEntity(uri, entity); } - /** see {@link ContentProvider#bulkUpdateEntities} */ - public int[] bulkUpdateEntities(Uri uri, Entity[] entities) throws RemoteException { - return mContentProvider.bulkUpdateEntities(uri, entities); + /** see {@link ContentProvider#applyBatch} */ + public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + throws RemoteException, OperationApplicationException { + return mContentProvider.applyBatch(operations); } /** diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index ac67076..4747726 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -154,37 +154,36 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return true; } - case BULK_INSERT_ENTITIES_TRANSACTION: + case INSERT_ENTITIES_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri uri = Uri.CREATOR.createFromParcel(data); - String className = data.readString(); - Class entityClass = Class.forName(className); - int numEntities = data.readInt(); - Entity[] entities = new Entity[numEntities]; - for (int i = 0; i < numEntities; i++) { - entities[i] = (Entity) data.readParcelable(entityClass.getClassLoader()); - } - Uri[] uris = bulkInsertEntities(uri, entities); + Entity entity = (Entity) data.readParcelable(null); + Uri newUri = insertEntity(uri, entity); reply.writeNoException(); - reply.writeTypedArray(uris, 0); + Uri.writeToParcel(reply, newUri); return true; } - case BULK_UPDATE_ENTITIES_TRANSACTION: + case UPDATE_ENTITIES_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri uri = Uri.CREATOR.createFromParcel(data); - String className = data.readString(); - Class entityClass = Class.forName(className); - int numEntities = data.readInt(); - Entity[] entities = new Entity[numEntities]; - for (int i = 0; i < numEntities; i++) { - entities[i] = (Entity) data.readParcelable(entityClass.getClassLoader()); - } - int[] counts = bulkUpdateEntities(uri, entities); + Entity entity = (Entity) data.readParcelable(null); + int count = updateEntity(uri, entity); + reply.writeNoException(); + reply.writeInt(count); + return true; + } + + case APPLY_BATCH_TRANSACTION: + { + data.enforceInterface(IContentProvider.descriptor); + final ContentProviderOperation[] operations = + data.createTypedArray(ContentProviderOperation.CREATOR); + final ContentProviderResult[] results = applyBatch(operations); reply.writeNoException(); - reply.writeIntArray(counts); + reply.writeTypedArray(results, 0); return true; } @@ -472,23 +471,18 @@ final class ContentProviderProxy implements IContentProvider return count; } - public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) throws RemoteException { + public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + throws RemoteException, OperationApplicationException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); - uri.writeToParcel(data, 0); - data.writeString(entities[0].getClass().getName()); - data.writeInt(entities.length); - for (Entity entity : entities) { - data.writeParcelable(entity, 0); - } + data.writeTypedArray(operations, 0); + mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); - mRemote.transact(IContentProvider.BULK_INSERT_ENTITIES_TRANSACTION, data, reply, 0); - - DatabaseUtils.readExceptionFromParcel(reply); - Uri[] results = new Uri[entities.length]; - reply.readTypedArray(results, Uri.CREATOR); + DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); + final ContentProviderResult[] results = + reply.createTypedArray(ContentProviderResult.CREATOR); data.recycle(); reply.recycle(); @@ -496,28 +490,42 @@ final class ContentProviderProxy implements IContentProvider return results; } - public int[] bulkUpdateEntities(Uri uri, Entity[] entities) throws RemoteException { + public Uri insertEntity(Uri uri, Entity entity) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(IContentProvider.descriptor); - uri.writeToParcel(data, 0); - data.writeString(entities[0].getClass().getName()); - data.writeInt(entities.length); - for (Entity entity : entities) { + try { + data.writeInterfaceToken(IContentProvider.descriptor); + uri.writeToParcel(data, 0); data.writeParcelable(entity, 0); + + mRemote.transact(IContentProvider.INSERT_ENTITIES_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + return Uri.CREATOR.createFromParcel(reply); + } finally { + data.recycle(); + reply.recycle(); } + } - mRemote.transact(IContentProvider.BULK_UPDATE_ENTITIES_TRANSACTION, data, reply, 0); + public int updateEntity(Uri uri, Entity entity) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); - DatabaseUtils.readExceptionFromParcel(reply); - int[] results = new int[entities.length]; - reply.readIntArray(results); + try { + data.writeInterfaceToken(IContentProvider.descriptor); + uri.writeToParcel(data, 0); + data.writeParcelable(entity, 0); - data.recycle(); - reply.recycle(); + mRemote.transact(IContentProvider.UPDATE_ENTITIES_TRANSACTION, data, reply, 0); - return results; + DatabaseUtils.readExceptionFromParcel(reply); + return reply.readInt(); + } finally { + data.recycle(); + reply.recycle(); + } } public int delete(Uri url, String selection, String[] selectionArgs) diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index 148cc35..ce92198 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -18,10 +18,15 @@ package android.content; import android.net.Uri; import android.database.Cursor; +import android.os.Parcelable; +import android.os.Parcel; import java.util.Map; +import java.util.HashMap; -public class ContentProviderOperation { +import dalvik.system.VMStack; + +public class ContentProviderOperation implements Parcelable { private final static int TYPE_INSERT = 1; private final static int TYPE_UPDATE = 2; private final static int TYPE_DELETE = 3; @@ -32,6 +37,7 @@ public class ContentProviderOperation { private final String mSelection; private final String[] mSelectionArgs; private final ContentValues mValues; + private final Entity mEntity; private final Integer mExpectedCount; private final ContentValues mValuesBackReferences; private final Map<Integer, Integer> mSelectionArgsBackReferences; @@ -46,6 +52,7 @@ public class ContentProviderOperation { mType = builder.mType; mUri = builder.mUri; mValues = builder.mValues; + mEntity = builder.mEntity; mSelection = builder.mSelection; mSelectionArgs = builder.mSelectionArgs; mExpectedCount = builder.mExpectedCount; @@ -53,6 +60,75 @@ public class ContentProviderOperation { mValuesBackReferences = builder.mValuesBackReferences; } + private ContentProviderOperation(Parcel source, ClassLoader classLoader) { + mType = source.readInt(); + mUri = Uri.CREATOR.createFromParcel(source); + mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null; + mEntity = (Entity) source.readParcelable(classLoader); + mSelection = source.readInt() != 0 ? source.readString() : null; + mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null; + mExpectedCount = source.readInt() != 0 ? source.readInt() : null; + mValuesBackReferences = source.readInt() != 0 + + ? ContentValues.CREATOR.createFromParcel(source) + : null; + mSelectionArgsBackReferences = source.readInt() != 0 + ? new HashMap<Integer, Integer>() + : null; + if (mSelectionArgsBackReferences != null) { + final int count = source.readInt(); + for (int i = 0; i < count; i++) { + mSelectionArgsBackReferences.put(source.readInt(), source.readInt()); + } + } + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + Uri.writeToParcel(dest, mUri); + if (mValues != null) { + dest.writeInt(1); + mValues.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } + dest.writeParcelable(mEntity, 0); + if (mSelection != null) { + dest.writeInt(1); + dest.writeString(mSelection); + } else { + dest.writeInt(0); + } + if (mSelectionArgs != null) { + dest.writeInt(1); + dest.writeStringArray(mSelectionArgs); + } else { + dest.writeInt(0); + } + if (mExpectedCount != null) { + dest.writeInt(1); + dest.writeInt(mExpectedCount); + } else { + dest.writeInt(0); + } + if (mValuesBackReferences != null) { + dest.writeInt(1); + mValuesBackReferences.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } + if (mSelectionArgsBackReferences != null) { + dest.writeInt(1); + dest.writeInt(mSelectionArgsBackReferences.size()); + for (Map.Entry<Integer, Integer> entry : mSelectionArgsBackReferences.entrySet()) { + dest.writeInt(entry.getKey()); + dest.writeInt(entry.getValue()); + } + } else { + dest.writeInt(0); + } + } + /** * Create a {@link Builder} suitable for building an insert {@link ContentProviderOperation}. * @param uri The {@link Uri} that is the target of the insert. @@ -92,6 +168,18 @@ public class ContentProviderOperation { return new Builder(TYPE_COUNT, uri); } + public Uri getUri() { + return mUri; + } + + public boolean isWriteOperation() { + return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE; + } + + public boolean isReadOperation() { + return mType == TYPE_COUNT; + } + /** * Applies this operation using the given provider. The backRefs array is used to resolve any * back references that were requested using @@ -113,7 +201,12 @@ public class ContentProviderOperation { resolveSelectionArgsBackReferences(backRefs, numBackRefs); if (mType == TYPE_INSERT) { - Uri newUri = provider.insert(mUri, values); + Uri newUri; + if (mEntity != null) { + newUri = provider.insertEntity(mUri, mEntity); + } else { + newUri = provider.insert(mUri, values); + } if (newUri == null) { throw new OperationApplicationException("insert failed"); } @@ -124,7 +217,11 @@ public class ContentProviderOperation { if (mType == TYPE_DELETE) { numRows = provider.delete(mUri, mSelection, selectionArgs); } else if (mType == TYPE_UPDATE) { - numRows = provider.update(mUri, values, mSelection, selectionArgs); + if (mEntity != null) { + numRows = provider.updateEntity(mUri, mEntity); + } else { + numRows = provider.update(mUri, values, mSelection, selectionArgs); + } } else if (mType == TYPE_COUNT) { Cursor cursor = provider.query(mUri, COUNT_COLUMNS, mSelection, selectionArgs, null); try { @@ -239,6 +336,22 @@ public class ContentProviderOperation { return backRefValue; } + public int describeContents() { + return 0; + } + + public static final Creator<ContentProviderOperation> CREATOR = + new Creator<ContentProviderOperation>() { + public ContentProviderOperation createFromParcel(Parcel source) { + return new ContentProviderOperation(source, VMStack.getCallingClassLoader2()); + } + + public ContentProviderOperation[] newArray(int size) { + return new ContentProviderOperation[size]; + } + }; + + /** * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)}, @@ -255,6 +368,7 @@ public class ContentProviderOperation { private String mSelection; private String[] mSelectionArgs; private ContentValues mValues; + private Entity mEntity; private Integer mExpectedCount; private ContentValues mValuesBackReferences; private Map<Integer, Integer> mSelectionArgsBackReferences; @@ -268,8 +382,16 @@ public class ContentProviderOperation { mUri = uri; } - /** Create a ContentroviderOperation from this {@link Builder}. */ + /** Create a ContentProviderOperation from this {@link Builder}. */ public ContentProviderOperation build() { + if (mValues != null && mEntity != null) { + throw new IllegalArgumentException("you are not allowed to specify both an entity " + + "and a values"); + } + if (mEntity != null && mValuesBackReferences != null) { + throw new IllegalArgumentException("you are not allowed to specify both an entity " + + "and a values backreference"); + } return new ContentProviderOperation(this); } @@ -295,7 +417,7 @@ public class ContentProviderOperation { * Add a {@link Map} of back references. The integer key is the index of the selection arg * to set and the integer value is the index of the previous result whose * value should be used for the arg. If any value at that index of the selection arg - * that was specified by {@likn withSelection} will be overwritten. + * that was specified by {@link #withSelection} will be overwritten. * This can only be used with builders of type update, delete, or count query. * @return this builder, to allow for chaining. */ @@ -323,6 +445,20 @@ public class ContentProviderOperation { } /** + * The ContentValues to use. This may be null. These values may be overwritten by + * the corresponding value specified by {@link #withValueBackReferences(ContentValues)}. + * This can only be used with builders of type insert or update. + * @return this builder, to allow for chaining. + */ + public Builder withEntity(Entity entity) { + if (mType != TYPE_INSERT && mType != TYPE_UPDATE) { + throw new IllegalArgumentException("only inserts and updates can have an entity"); + } + mEntity = entity; + return this; + } + + /** * The selection and arguments to use. An occurrence of '?' in the selection will be * replaced with the corresponding occurence of the selection argument. Any of the * selection arguments may be overwritten by a selection argument back reference as diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java index 2e34e40..34aaa6d 100644 --- a/core/java/android/content/ContentProviderResult.java +++ b/core/java/android/content/ContentProviderResult.java @@ -17,12 +17,14 @@ package android.content; import android.net.Uri; +import android.os.Parcelable; +import android.os.Parcel; /** * Contains the result of the application of a {@link ContentProviderOperation}. It is guaranteed * to have exactly one of {@link #uri} or {@link #count} set. */ -public class ContentProviderResult { +public class ContentProviderResult implements Parcelable { public final Uri uri; public final Integer count; @@ -36,4 +38,40 @@ public class ContentProviderResult { this.count = count; this.uri = null; } + + public ContentProviderResult(Parcel source) { + int type = source.readInt(); + if (type == 1) { + count = source.readInt(); + uri = null; + } else { + count = null; + uri = Uri.CREATOR.createFromParcel(source); + } + } + + public void writeToParcel(Parcel dest, int flags) { + if (uri == null) { + dest.writeInt(1); + dest.writeInt(count); + } else { + dest.writeInt(2); + uri.writeToParcel(dest, 0); + } + } + + public int describeContents() { + return 0; + } + + public static final Creator<ContentProviderResult> CREATOR = + new Creator<ContentProviderResult>() { + public ContentProviderResult createFromParcel(Parcel source) { + return new ContentProviderResult(source); + } + + public ContentProviderResult[] newArray(int size) { + return new ContentProviderResult[size]; + } + }; }
\ No newline at end of file diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index f9bed59..f7b52fa 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -166,35 +166,69 @@ public abstract class ContentResolver { } } - class EntityIteratorWrapper implements EntityIterator { + /** + * EntityIterator wrapper that releases the associated ContentProviderClient when the + * iterator is closed. + */ + private class EntityIteratorWrapper implements EntityIterator { private final EntityIterator mInner; private final ContentProviderClient mClient; + private volatile boolean mClientReleased; EntityIteratorWrapper(EntityIterator inner, ContentProviderClient client) { mInner = inner; mClient = client; + mClientReleased = false; } public boolean hasNext() throws RemoteException { + if (mClientReleased) { + throw new IllegalStateException("this iterator is already closed"); + } return mInner.hasNext(); } public Entity next() throws RemoteException { + if (mClientReleased) { + throw new IllegalStateException("this iterator is already closed"); + } return mInner.next(); } public void close() { mClient.release(); mInner.close(); + mClientReleased = true; } protected void finalize() throws Throwable { - close(); + if (!mClientReleased) { + mClient.release(); + } super.finalize(); } } - public final EntityIterator queryEntity(Uri uri, + /** + * Query the given URI, returning an {@link EntityIterator} over the result set. + * + * @param uri The URI, using the content:// scheme, for the content to + * retrieve. + * @param selection A filter declaring which rows to return, formatted as an + * SQL WHERE clause (excluding the WHERE itself). Passing null will + * return all rows for the given URI. + * @param selectionArgs You may include ?s in selection, which will be + * replaced by the values from selectionArgs, in the order that they + * appear in the selection. The values will be bound as Strings. + * @param sortOrder How to order the rows, formatted as an SQL ORDER BY + * clause (excluding the ORDER BY itself). Passing null will use the + * default sort order, which may be unordered. + * @return An EntityIterator object + * @throws RemoteException thrown if a RemoteException is encountered while attempting + * to communicate with a remote provider. + * @throws IllegalArgumentException thrown if there is no provider that matches the uri + */ + public final EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs, String sortOrder) throws RemoteException { ContentProviderClient provider = acquireContentProviderClient(uri); if (provider == null) { @@ -532,6 +566,16 @@ public abstract class ContentResolver { } } + /** + * Inserts an Entity at the given URL. + * + * @param uri The URL of the table to insert into. + * @param entity an Entity to insert at uri. This must be the same Entity subtype that the + * matching content provider expects at that uri. + * @return the URL of the newly created row. + * @throws RemoteException thrown if a RemoteException is encountered while attempting + * to communicate with a remote provider. + */ public final Uri insertEntity(Uri uri, Entity entity) throws RemoteException { ContentProviderClient provider = acquireContentProviderClient(uri); if (provider == null) { @@ -544,6 +588,16 @@ public abstract class ContentResolver { } } + /** + * Replaces the Entity at the given URL with the provided entity. + * + * @param uri The URL of the entity to update. + * @param entity the new version of the entity + * @return the number of rows that are updated. Will be 0 if an entity no longer exists + * at uri otherwise it will be 1. + * @throws RemoteException thrown if a RemoteException is encountered while attempting + * to communicate with a remote provider. + */ public final int updateEntity(Uri uri, Entity entity) throws RemoteException { ContentProviderClient provider = acquireContentProviderClient(uri); if (provider == null) { @@ -556,27 +610,31 @@ public abstract class ContentResolver { } } - public final Uri[] bulkInsertEntities(Uri uri, Entity[] entities) - throws RemoteException { - ContentProviderClient provider = acquireContentProviderClient(uri); - if (provider == null) { - throw new IllegalArgumentException("Unknown URL " + uri); - } - try { - return provider.bulkInsertEntities(uri, entities); - } finally { - provider.release(); - } - } - - public final int[] bulkUpdateEntities(Uri uri, Entity[] entities) - throws RemoteException { - ContentProviderClient provider = acquireContentProviderClient(uri); + /** + * Applies each of the {@link ContentProviderOperation} objects and returns an array + * of their results. Passes through OperationApplicationException, which may be thrown + * by the call to {@link ContentProviderOperation#apply}. + * If all the applications succeed then a {@link ContentProviderResult} array with the + * same number of elements as the operations will be returned. It is implementation-specific + * how many, if any, operations will have been successfully applied if a call to + * apply results in a {@link OperationApplicationException}. + * @param authority the authority of the ContentProvider to which this batch should be applied + * @param operations the operations to apply + * @return the results of the applications + * @throws OperationApplicationException thrown if an application fails. + * See {@link ContentProviderOperation#apply} for more information. + * @throws RemoteException thrown if a RemoteException is encountered while attempting + * to communicate with a remote provider. + */ + public ContentProviderResult[] applyBatch(String authority, + ContentProviderOperation[] operations) + throws RemoteException, OperationApplicationException { + ContentProviderClient provider = acquireContentProviderClient(authority); if (provider == null) { - throw new IllegalArgumentException("Unknown URL " + uri); + throw new IllegalArgumentException("Unknown authority " + authority); } try { - return provider.bulkUpdateEntities(uri, entities); + return provider.applyBatch(operations); } finally { provider.release(); } diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index b4d1865..f82c982 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -50,16 +50,18 @@ public interface IContentProvider extends IInterface { public Uri insert(Uri url, ContentValues initialValues) throws RemoteException; public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException; - public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) throws RemoteException; + public Uri insertEntity(Uri uri, Entity entities) throws RemoteException; public int delete(Uri url, String selection, String[] selectionArgs) throws RemoteException; public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) throws RemoteException; - public int[] bulkUpdateEntities(Uri uri, Entity[] entities) throws RemoteException; + public int updateEntity(Uri uri, Entity entity) throws RemoteException; public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException, FileNotFoundException; public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException, FileNotFoundException; + public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) + throws RemoteException, OperationApplicationException; /* IPC constants */ static final String descriptor = "android.content.IContentProvider"; @@ -72,7 +74,8 @@ public interface IContentProvider extends IInterface { static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12; static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13; static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14; - static final int BULK_INSERT_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 16; - static final int BULK_UPDATE_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 17; + static final int INSERT_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 16; + static final int UPDATE_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 17; static final int QUERY_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 18; + static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19; } diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java index eb800f7..1887df2 100644 --- a/core/java/android/content/IEntityIterator.java +++ b/core/java/android/content/IEntityIterator.java @@ -25,6 +25,7 @@ import android.util.Log; /** * ICPC interface methods for an iterator over Entity objects. + * @hide */ public interface IEntityIterator extends IInterface { /** Local-side IPC implementation stub class. */ @@ -92,7 +93,6 @@ public interface IEntityIterator extends IInterface { return true; } reply.writeNoException(); - reply.writeString(entity.getClass().getName()); reply.writeParcelable(entity, 0); return true; } @@ -145,22 +145,15 @@ public interface IEntityIterator extends IInterface { public Entity next() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); - Entity _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0); _reply.readException(); - final String className = _reply.readString(); - final Class entityClass = Class.forName(className); - ClassLoader classLoader = entityClass.getClassLoader(); - _result = (Entity) _reply.readParcelable(classLoader); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); + return (Entity) _reply.readParcelable(null /* classLoader */); } finally { _reply.recycle(); _data.recycle(); } - return _result; } public void close() throws RemoteException { diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index 78ef374..4ca6601 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -20,6 +20,7 @@ import org.apache.commons.codec.binary.Hex; import android.content.ContentValues; import android.content.Context; +import android.content.OperationApplicationException; import android.database.sqlite.SQLiteAbortException; import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; @@ -82,6 +83,8 @@ public class DatabaseUtils { code = 8; } else if (e instanceof SQLiteException) { code = 9; + } else if (e instanceof OperationApplicationException) { + code = 10; } else { reply.writeException(e); Log.e(TAG, "Writing exception to parcel", e); @@ -123,6 +126,18 @@ public class DatabaseUtils { } } + public static void readExceptionWithOperationApplicationExceptionFromParcel( + Parcel reply) throws OperationApplicationException { + int code = reply.readInt(); + if (code == 0) return; + String msg = reply.readString(); + if (code == 10) { + throw new OperationApplicationException(msg); + } else { + DatabaseUtils.readExceptionFromParcel(reply, msg, code); + } + } + private static final void readExceptionFromParcel(Parcel reply, String msg, int code) { switch (code) { case 2: |