summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml362
-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
-rw-r--r--test-runner/android/test/mock/MockContentProvider.java13
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/content/ContentProviderOperationTest.java211
-rw-r--r--tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java508
13 files changed, 1045 insertions, 524 deletions
diff --git a/api/current.xml b/api/current.xml
index 6179e4b..54d98a7 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -28053,36 +28053,6 @@
<parameter name="values" type="android.content.ContentValues[]">
</parameter>
</method>
-<method name="bulkInsertEntities"
- return="android.net.Uri[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-<parameter name="entities" type="android.content.Entity[]">
-</parameter>
-</method>
-<method name="bulkUpdateEntities"
- return="int[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-<parameter name="entities" type="android.content.Entity[]">
-</parameter>
-</method>
<method name="delete"
return="int"
abstract="true"
@@ -28382,8 +28352,8 @@
deprecated="not deprecated"
visibility="public"
>
-<method name="bulkInsert"
- return="int"
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
abstract="false"
native="false"
synchronized="false"
@@ -28392,32 +28362,15 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="url" type="android.net.Uri">
-</parameter>
-<parameter name="initialValues" type="android.content.ContentValues[]">
+<parameter name="operations" type="android.content.ContentProviderOperation[]">
</parameter>
-<exception name="RemoteException" type="android.os.RemoteException">
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
</exception>
-</method>
-<method name="bulkInsertEntities"
- return="android.net.Uri[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-<parameter name="entities" type="android.content.Entity[]">
-</parameter>
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
-<method name="bulkUpdateEntities"
- return="int[]"
+<method name="bulkInsert"
+ return="int"
abstract="false"
native="false"
synchronized="false"
@@ -28426,9 +28379,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="uri" type="android.net.Uri">
+<parameter name="url" type="android.net.Uri">
</parameter>
-<parameter name="entities" type="android.content.Entity[]">
+<parameter name="initialValues" type="android.content.ContentValues[]">
</parameter>
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
@@ -28652,6 +28605,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.os.Parcelable">
+</implements>
<method name="apply"
return="android.content.ContentProviderResult"
abstract="false"
@@ -28671,6 +28626,50 @@
<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
</exception>
</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isReadOperation"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isWriteOperation"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="newCountQuery"
return="android.content.ContentProviderOperation.Builder"
abstract="false"
@@ -28753,6 +28752,31 @@
<parameter name="numBackRefs" type="int">
</parameter>
</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="ContentProviderOperation.Builder"
extends="java.lang.Object"
@@ -28773,6 +28797,19 @@
visibility="public"
>
</method>
+<method name="withEntity"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+</method>
<method name="withExpectedCount"
return="android.content.ContentProviderOperation.Builder"
abstract="false"
@@ -28849,6 +28886,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.os.Parcelable">
+</implements>
<constructor name="ContentProviderResult"
type="android.content.ContentProviderResult"
static="false"
@@ -28869,6 +28908,52 @@
<parameter name="count" type="int">
</parameter>
</constructor>
+<constructor name="ContentProviderResult"
+ type="android.content.ContentProviderResult"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="count"
type="java.lang.Integer"
transient="false"
@@ -29018,40 +29103,27 @@
<parameter name="name" type="java.lang.String">
</parameter>
</method>
-<method name="bulkInsert"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="url" type="android.net.Uri">
-</parameter>
-<parameter name="values" type="android.content.ContentValues[]">
-</parameter>
-</method>
-<method name="bulkInsertEntities"
- return="android.net.Uri[]"
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
abstract="false"
native="false"
synchronized="false"
static="false"
- final="true"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="uri" type="android.net.Uri">
+<parameter name="authority" type="java.lang.String">
</parameter>
-<parameter name="entities" type="android.content.Entity[]">
+<parameter name="operations" type="android.content.ContentProviderOperation[]">
</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
-<method name="bulkUpdateEntities"
- return="int[]"
+<method name="bulkInsert"
+ return="int"
abstract="false"
native="false"
synchronized="false"
@@ -29060,12 +29132,10 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="uri" type="android.net.Uri">
+<parameter name="url" type="android.net.Uri">
</parameter>
-<parameter name="entities" type="android.content.Entity[]">
+<parameter name="values" type="android.content.ContentValues[]">
</parameter>
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
</method>
<method name="cancelSync"
return="void"
@@ -29276,7 +29346,7 @@
<parameter name="sortOrder" type="java.lang.String">
</parameter>
</method>
-<method name="queryEntity"
+<method name="queryEntities"
return="android.content.EntityIterator"
abstract="false"
native="false"
@@ -32604,119 +32674,6 @@
</exception>
</method>
</interface>
-<interface name="IEntityIterator"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.IInterface">
-</implements>
-<method name="close"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
-<method name="hasNext"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
-<method name="next"
- return="android.content.Entity"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
-</interface>
-<class name="IEntityIterator.Stub"
- extends="android.os.Binder"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.content.IEntityIterator">
-</implements>
-<constructor name="IEntityIterator.Stub"
- type="android.content.IEntityIterator.Stub"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="asBinder"
- return="android.os.IBinder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="asInterface"
- return="android.content.IEntityIterator"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="obj" type="android.os.IBinder">
-</parameter>
-</method>
-<method name="onTransact"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="code" type="int">
-</parameter>
-<parameter name="data" type="android.os.Parcel">
-</parameter>
-<parameter name="reply" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-<exception name="RemoteException" type="android.os.RemoteException">
-</exception>
-</method>
-</class>
<class name="Intent"
extends="java.lang.Object"
abstract="false"
@@ -46712,6 +46669,21 @@
<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
</exception>
</method>
+<method name="readExceptionWithOperationApplicationExceptionFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+</method>
<method name="sqlEscapeString"
return="java.lang.String"
abstract="false"
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:
diff --git a/test-runner/android/test/mock/MockContentProvider.java b/test-runner/android/test/mock/MockContentProvider.java
index fdba2e2..757c72d 100644
--- a/test-runner/android/test/mock/MockContentProvider.java
+++ b/test-runner/android/test/mock/MockContentProvider.java
@@ -20,6 +20,9 @@ import android.content.ContentValues;
import android.content.IContentProvider;
import android.content.Entity;
import android.content.EntityIterator;
+import android.content.ContentProviderResult;
+import android.content.ContentProviderOperation;
+import android.content.OperationApplicationException;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.CursorWindow;
@@ -49,7 +52,7 @@ public class MockContentProvider implements IContentProvider {
return 0;
}
- public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) throws RemoteException {
+ public Uri insertEntity(Uri uri, Entity entities) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -87,7 +90,11 @@ public class MockContentProvider implements IContentProvider {
throws FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
-
+
+ public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) throws RemoteException, OperationApplicationException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
@SuppressWarnings("unused")
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
String sortOrder) throws RemoteException {
@@ -105,7 +112,7 @@ public class MockContentProvider implements IContentProvider {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public int[] bulkUpdateEntities(Uri uri, Entity[] entities) throws RemoteException {
+ public int updateEntity(Uri uri, Entity entity) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ContentProviderOperationTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/ContentProviderOperationTest.java
deleted file mode 100644
index 46a12fd..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/content/ContentProviderOperationTest.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests.content;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.net.Uri;
-import android.content.ContentValues;
-import android.content.ContentProviderOperation;
-import android.content.OperationApplicationException;
-import android.content.ContentProviderResult;
-import android.content.ContentProvider;
-import android.text.TextUtils;
-import android.database.Cursor;
-import junit.framework.TestCase;
-
-import java.util.Map;
-import java.util.Hashtable;
-
-@SmallTest
-public class ContentProviderOperationTest extends TestCase {
- private final static Uri sTestUri1 = Uri.parse("content://authority/blah");
- private final static ContentValues sTestValues1;
-
- static {
- sTestValues1 = new ContentValues();
- sTestValues1.put("a", 1);
- sTestValues1.put("b", "two");
- }
-
- public void testInsert() throws OperationApplicationException {
- ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
- .withValues(sTestValues1)
- .build();
- ContentProviderResult result = op1.apply(new TestContentProvider() {
- public Uri insert(Uri uri, ContentValues values) {
- assertEquals(sTestUri1.toString(), uri.toString());
- assertEquals(sTestValues1.toString(), values.toString());
- return uri.buildUpon().appendPath("19").build();
- }
- }, null, 0);
- assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
- }
-
- public void testInsertNoValues() throws OperationApplicationException {
- ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
- .build();
- ContentProviderResult result = op1.apply(new TestContentProvider() {
- public Uri insert(Uri uri, ContentValues values) {
- assertEquals(sTestUri1.toString(), uri.toString());
- assertNull(values);
- return uri.buildUpon().appendPath("19").build();
- }
- }, null, 0);
- assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
- }
-
- public void testInsertFailed() {
- ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
- .withValues(sTestValues1)
- .build();
- try {
- op1.apply(new TestContentProvider() {
- public Uri insert(Uri uri, ContentValues values) {
- assertEquals(sTestUri1.toString(), uri.toString());
- assertEquals(sTestValues1.toString(), values.toString());
- return null;
- }
- }, null, 0);
- fail("the apply should have thrown an OperationApplicationException");
- } catch (OperationApplicationException e) {
- // this is the expected case
- }
- }
-
- public void testInsertWithBackRefs() throws OperationApplicationException {
- ContentValues valuesBackRefs = new ContentValues();
- valuesBackRefs.put("a1", 3);
- valuesBackRefs.put("a2", 1);
-
- ContentProviderResult[] previousResults = new ContentProviderResult[4];
- previousResults[0] = new ContentProviderResult(100);
- previousResults[1] = new ContentProviderResult(101);
- previousResults[2] = new ContentProviderResult(102);
- previousResults[3] = new ContentProviderResult(103);
- ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
- .withValues(sTestValues1)
- .withValueBackReferences(valuesBackRefs)
- .build();
- ContentProviderResult result = op1.apply(new TestContentProvider() {
- public Uri insert(Uri uri, ContentValues values) {
- assertEquals(sTestUri1.toString(), uri.toString());
- ContentValues expected = new ContentValues(sTestValues1);
- expected.put("a1", 103);
- expected.put("a2", 101);
- assertEquals(expected.toString(), values.toString());
- return uri.buildUpon().appendPath("19").build();
- }
- }, previousResults, previousResults.length);
- assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
- }
-
- public void testUpdate() throws OperationApplicationException {
- ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
- .withValues(sTestValues1)
- .build();
- ContentProviderResult[] backRefs = new ContentProviderResult[2];
- ContentProviderResult result = op1.apply(new TestContentProvider() {
- public Uri insert(Uri uri, ContentValues values) {
- assertEquals(sTestUri1.toString(), uri.toString());
- assertEquals(sTestValues1.toString(), values.toString());
- return uri.buildUpon().appendPath("19").build();
- }
- }, backRefs, 1);
- assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
- }
-
- public void testValueBackRefs() {
- ContentValues values = new ContentValues();
- values.put("a", "in1");
- values.put("a2", "in2");
- values.put("b", "in3");
- values.put("c", "in4");
-
- ContentProviderResult[] previousResults = new ContentProviderResult[4];
- previousResults[0] = new ContentProviderResult(100);
- previousResults[1] = new ContentProviderResult(101);
- previousResults[2] = new ContentProviderResult(102);
- previousResults[3] = new ContentProviderResult(103);
-
- ContentValues valuesBackRefs = new ContentValues();
- valuesBackRefs.put("a1", 3); // a1 -> 103
- valuesBackRefs.put("a2", 1); // a2 -> 101
- valuesBackRefs.put("a3", 2); // a3 -> 102
-
- ContentValues expectedValues = new ContentValues(values);
- expectedValues.put("a1", "103");
- expectedValues.put("a2", "101");
- expectedValues.put("a3", "102");
-
- ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
- .withValues(values)
- .withValueBackReferences(valuesBackRefs)
- .build();
- ContentValues v2 = op1.resolveValueBackReferences(previousResults, previousResults.length);
- assertEquals(expectedValues, v2);
- }
-
- public void testSelectionBackRefs() {
- Map<Integer, Integer> selectionBackRefs = new Hashtable<Integer, Integer>();
- selectionBackRefs.put(1, 3);
- selectionBackRefs.put(2, 1);
- selectionBackRefs.put(4, 2);
-
- ContentProviderResult[] previousResults = new ContentProviderResult[4];
- previousResults[0] = new ContentProviderResult(100);
- previousResults[1] = new ContentProviderResult(101);
- previousResults[2] = new ContentProviderResult(102);
- previousResults[3] = new ContentProviderResult(103);
-
- String[] selectionArgs = new String[]{"a", null, null, "b", null};
-
- ContentProviderOperation op1 = ContentProviderOperation.newUpdate(sTestUri1)
- .withSelectionBackReferences(selectionBackRefs)
- .withSelection("unused", selectionArgs)
- .build();
- String[] s2 = op1.resolveSelectionArgsBackReferences(
- previousResults, previousResults.length);
- assertEquals("a,103,101,b,102", TextUtils.join(",", s2));
- }
-
- static class TestContentProvider extends ContentProvider {
- public boolean onCreate() {
- throw new UnsupportedOperationException();
- }
-
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- throw new UnsupportedOperationException();
- }
-
- public String getType(Uri uri) {
- throw new UnsupportedOperationException();
- }
-
- public Uri insert(Uri uri, ContentValues values) {
- throw new UnsupportedOperationException();
- }
-
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException();
- }
-
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException();
- }
- }
-} \ No newline at end of file
diff --git a/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
new file mode 100644
index 0000000..27241b8
--- /dev/null
+++ b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+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.Hashtable;
+import java.util.Map;
+
+@SmallTest
+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);
+ sTestValues1.put("b", "two");
+ }
+
+ public void testInsert() throws OperationApplicationException {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .build();
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertEquals(sTestValues1.toString(), values.toString());
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, null, 0);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testInsertNoValues() throws OperationApplicationException {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .build();
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertNull(values);
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, null, 0);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testInsertFailed() {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .build();
+ try {
+ op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertEquals(sTestValues1.toString(), values.toString());
+ return null;
+ }
+ }, null, 0);
+ fail("the apply should have thrown an OperationApplicationException");
+ } catch (OperationApplicationException e) {
+ // this is the expected case
+ }
+ }
+
+ public void testInsertWithBackRefs() throws OperationApplicationException {
+ ContentValues valuesBackRefs = new ContentValues();
+ valuesBackRefs.put("a1", 3);
+ valuesBackRefs.put("a2", 1);
+
+ ContentProviderResult[] previousResults = new ContentProviderResult[4];
+ previousResults[0] = new ContentProviderResult(100);
+ previousResults[1] = new ContentProviderResult(101);
+ previousResults[2] = new ContentProviderResult(102);
+ previousResults[3] = new ContentProviderResult(103);
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .withValueBackReferences(valuesBackRefs)
+ .build();
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ ContentValues expected = new ContentValues(sTestValues1);
+ expected.put("a1", 103);
+ expected.put("a2", 101);
+ assertEquals(expected.toString(), values.toString());
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, previousResults, previousResults.length);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testUpdate() throws OperationApplicationException {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .build();
+ ContentProviderResult[] backRefs = new ContentProviderResult[2];
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertEquals(sTestValues1.toString(), values.toString());
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, backRefs, 1);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testValueBackRefs() {
+ ContentValues values = new ContentValues();
+ values.put("a", "in1");
+ values.put("a2", "in2");
+ values.put("b", "in3");
+ values.put("c", "in4");
+
+ ContentProviderResult[] previousResults = new ContentProviderResult[4];
+ previousResults[0] = new ContentProviderResult(100);
+ previousResults[1] = new ContentProviderResult(101);
+ previousResults[2] = new ContentProviderResult(102);
+ previousResults[3] = new ContentProviderResult(103);
+
+ ContentValues valuesBackRefs = new ContentValues();
+ valuesBackRefs.put("a1", 3); // a1 -> 103
+ valuesBackRefs.put("a2", 1); // a2 -> 101
+ valuesBackRefs.put("a3", 2); // a3 -> 102
+
+ ContentValues expectedValues = new ContentValues(values);
+ expectedValues.put("a1", "103");
+ expectedValues.put("a2", "101");
+ expectedValues.put("a3", "102");
+
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(values)
+ .withValueBackReferences(valuesBackRefs)
+ .build();
+ ContentValues v2 = op1.resolveValueBackReferences(previousResults, previousResults.length);
+ assertEquals(expectedValues, v2);
+ }
+
+ public void testSelectionBackRefs() {
+ Map<Integer, Integer> selectionBackRefs = new Hashtable<Integer, Integer>();
+ selectionBackRefs.put(1, 3);
+ selectionBackRefs.put(2, 1);
+ selectionBackRefs.put(4, 2);
+
+ ContentProviderResult[] previousResults = new ContentProviderResult[4];
+ previousResults[0] = new ContentProviderResult(100);
+ previousResults[1] = new ContentProviderResult(101);
+ previousResults[2] = new ContentProviderResult(102);
+ previousResults[3] = new ContentProviderResult(103);
+
+ String[] selectionArgs = new String[]{"a", null, null, "b", null};
+
+ ContentProviderOperation op1 = ContentProviderOperation.newUpdate(sTestUri1)
+ .withSelectionBackReferences(selectionBackRefs)
+ .withSelection("unused", selectionArgs)
+ .build();
+ String[] s2 = op1.resolveSelectionArgsBackReferences(
+ previousResults, previousResults.length);
+ 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"));
+
+ builderSetEntity(builder, new TestEntity("blah"));
+ 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(1 /* ContentProviderOperation.TYPE_INSERT */, operationGetType(op2));
+ assertEquals("content://goo/bar", operationGetUri(op2).toString());
+ assertEquals("blah", ((TestEntity) operationGetEntity(op2)).mValue);
+ 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(2 /* ContentProviderOperation.TYPE_UPDATE */, operationGetType(op2));
+ assertEquals("content://goo/bar", operationGetUri(op2).toString());
+ assertNull(operationGetEntity(op2));
+ 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(3 /* ContentProviderOperation.TYPE_DELETE */, operationGetType(op2));
+ assertEquals("content://goo/bar", operationGetUri(op2).toString());
+ assertNull(operationGetEntity(op2));
+ 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 builderSetEntity(
+ ContentProviderOperation.Builder builder, Entity entity)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field;
+ field = CLASS_BUILDER.getDeclaredField("mEntity");
+ field.setAccessible(true);
+ field.set(builder, entity);
+ }
+
+ 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 Entity operationGetEntity(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mEntity");
+ field.setAccessible(true);
+ return (Entity) 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 static class TestEntity extends Entity {
+ public final String mValue;
+ public TestEntity(String value) {
+ mValue = value;
+ }
+
+ public TestEntity(Parcel source) {
+ mValue = source.readString();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mValue);
+ }
+
+ public static final Creator<TestEntity> CREATOR = new Creator<TestEntity>() {
+ public TestEntity createFromParcel(Parcel source) {
+ return new TestEntity(source);
+ }
+
+ public TestEntity[] newArray(int size) {
+ return new TestEntity[size];
+ }
+ };
+ }
+
+ public void testParcelingResult() {
+ Parcel parcel = Parcel.obtain();
+ ContentProviderResult result1;
+ ContentProviderResult result2;
+ try {
+ result1 = new ContentProviderResult(Uri.parse("content://goo/bar"));
+ result1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
+ assertEquals("content://goo/bar", result2.uri.toString());
+ assertNull(result2.count);
+ } finally {
+ parcel.recycle();
+ }
+
+ parcel = Parcel.obtain();
+ try {
+ result1 = new ContentProviderResult(42);
+ result1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
+ assertEquals(Integer.valueOf(42), result2.count);
+ assertNull(result2.uri);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ static class TestContentProvider extends ContentProvider {
+ public boolean onCreate() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+ }
+} \ No newline at end of file