summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/content/ContentProvider.java38
-rw-r--r--core/java/android/content/ContentProviderClient.java16
-rw-r--r--core/java/android/content/ContentProviderNative.java98
-rw-r--r--core/java/android/content/ContentProviderOperation.java146
-rw-r--r--core/java/android/content/ContentProviderResult.java40
-rw-r--r--core/java/android/content/ContentResolver.java100
-rw-r--r--core/java/android/content/IContentProvider.java11
-rw-r--r--core/java/android/content/IEntityIterator.java11
-rw-r--r--core/java/android/database/DatabaseUtils.java15
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: