summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorFred Quintana <fredq@google.com>2009-05-22 14:23:31 -0700
committerFred Quintana <fredq@google.com>2009-06-01 16:17:03 -0700
commit03d9490758c9318cee6d14d3cc5007556dce92d0 (patch)
tree58d56d848d0a1f0638157f0ba70afc9b75119601 /core
parentb0d031ad132dac585d1f21d46ebebcc4d13f40c7 (diff)
downloadframeworks_base-03d9490758c9318cee6d14d3cc5007556dce92d0.zip
frameworks_base-03d9490758c9318cee6d14d3cc5007556dce92d0.tar.gz
frameworks_base-03d9490758c9318cee6d14d3cc5007556dce92d0.tar.bz2
- create a new generic ISyncAdapter implementation, SyncAdapterNew
- change the applyBatch to take an ArrayList rather than an [] - change Entity to be a final flass that contains ContentValues - remove the ability to update/insert Entities by a ContentProviderOperation
Diffstat (limited to 'core')
-rw-r--r--core/java/android/content/AbstractSyncableContentProvider.java4
-rw-r--r--core/java/android/content/ContentProvider.java12
-rw-r--r--core/java/android/content/ContentProviderClient.java3
-rw-r--r--core/java/android/content/ContentProviderNative.java16
-rw-r--r--core/java/android/content/ContentProviderOperation.java82
-rw-r--r--core/java/android/content/ContentProviderResult.java7
-rw-r--r--core/java/android/content/ContentResolver.java47
-rw-r--r--core/java/android/content/Entity.java80
-rw-r--r--core/java/android/content/EntityIterator.java2
-rw-r--r--core/java/android/content/IContentProvider.java3
-rw-r--r--core/java/android/content/IEntityIterator.java5
-rw-r--r--core/java/android/content/SyncAdapterNew.java135
-rw-r--r--core/java/android/database/AbstractWindowedCursor.java42
-rw-r--r--core/java/android/database/CursorWindow.java51
-rw-r--r--core/java/android/provider/ContactsContract.java90
-rw-r--r--core/jni/android_database_CursorWindow.cpp65
16 files changed, 525 insertions, 119 deletions
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
index 05781f4..e628dcd 100644
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ b/core/java/android/content/AbstractSyncableContentProvider.java
@@ -406,7 +406,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
* }
* </pre>
*
- * @hide This method should be used only when {@link #applyBatch} is not enough and must be
+ * @hide This method should be used only when {@link ContentProvider#applyBatch} is not enough and must be
* used with {@link #endBatch}.
* e.g. If returned value has to be used during one transaction, this method might be useful.
*/
@@ -461,7 +461,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
}
}
- public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
boolean successful = false;
beginBatch();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index bb25b68..4e631c4 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -32,6 +32,7 @@ import android.os.ParcelFileDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* Content providers are one of the primary building blocks of Android applications, providing
@@ -156,7 +157,7 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.insertEntity(uri, entities);
}
- public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
for (ContentProviderOperation operation : operations) {
if (operation.isReadOperation()) {
@@ -641,11 +642,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
* @throws OperationApplicationException thrown if an application fails.
* See {@link ContentProviderOperation#apply} for more information.
*/
- public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
- ContentProviderResult[] results = new ContentProviderResult[operations.length];
- for (int i = 0; i < operations.length; i++) {
- results[i] = operations[i].apply(this, results, i);
+ final int numOperations = operations.size();
+ final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+ for (int i = 0; i < numOperations; i++) {
+ results[i] = operations.get(i).apply(this, results, i);
}
return results;
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 08d4fca..452653e 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -23,6 +23,7 @@ import android.os.ParcelFileDescriptor;
import android.content.res.AssetFileDescriptor;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* The public interface object used to interact with a {@link ContentProvider}. This is obtained by
@@ -105,7 +106,7 @@ public class ContentProviderClient {
}
/** see {@link ContentProvider#applyBatch} */
- public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+ public ContentProviderResult[] applyBatch(ArrayList<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 4747726..a4c217b 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -33,6 +33,7 @@ import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* {@hide}
@@ -179,8 +180,12 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
case APPLY_BATCH_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
- final ContentProviderOperation[] operations =
- data.createTypedArray(ContentProviderOperation.CREATOR);
+ final int numOperations = data.readInt();
+ final ArrayList<ContentProviderOperation> operations =
+ new ArrayList<ContentProviderOperation>(numOperations);
+ for (int i = 0; i < numOperations; i++) {
+ operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
+ }
final ContentProviderResult[] results = applyBatch(operations);
reply.writeNoException();
reply.writeTypedArray(results, 0);
@@ -471,13 +476,16 @@ final class ContentProviderProxy implements IContentProvider
return count;
}
- public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IContentProvider.descriptor);
- data.writeTypedArray(operations, 0);
+ data.writeInt(operations.size());
+ for (ContentProviderOperation operation : operations) {
+ operation.writeToParcel(data, 0);
+ }
mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply);
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index ce92198..07be017 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -24,8 +24,6 @@ import android.os.Parcel;
import java.util.Map;
import java.util.HashMap;
-import dalvik.system.VMStack;
-
public class ContentProviderOperation implements Parcelable {
private final static int TYPE_INSERT = 1;
private final static int TYPE_UPDATE = 2;
@@ -37,7 +35,6 @@ public class ContentProviderOperation implements Parcelable {
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;
@@ -52,7 +49,6 @@ public class ContentProviderOperation implements Parcelable {
mType = builder.mType;
mUri = builder.mUri;
mValues = builder.mValues;
- mEntity = builder.mEntity;
mSelection = builder.mSelection;
mSelectionArgs = builder.mSelectionArgs;
mExpectedCount = builder.mExpectedCount;
@@ -60,11 +56,10 @@ public class ContentProviderOperation implements Parcelable {
mValuesBackReferences = builder.mValuesBackReferences;
}
- private ContentProviderOperation(Parcel source, ClassLoader classLoader) {
+ private ContentProviderOperation(Parcel source) {
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;
@@ -92,7 +87,6 @@ public class ContentProviderOperation implements Parcelable {
} else {
dest.writeInt(0);
}
- dest.writeParcelable(mEntity, 0);
if (mSelection != null) {
dest.writeInt(1);
dest.writeString(mSelection);
@@ -184,7 +178,7 @@ public class ContentProviderOperation implements Parcelable {
* Applies this operation using the given provider. The backRefs array is used to resolve any
* back references that were requested using
* {@link Builder#withValueBackReferences(ContentValues)} and
- * {@link Builder#withSelectionBackReferences(java.util.Map)}.
+ * {@link Builder#withSelectionBackReference}.
* @param provider the {@link ContentProvider} on which this batch is applied
* @param backRefs a {@link ContentProviderResult} array that will be consulted
* to resolve any requested back references.
@@ -201,12 +195,7 @@ public class ContentProviderOperation implements Parcelable {
resolveSelectionArgsBackReferences(backRefs, numBackRefs);
if (mType == TYPE_INSERT) {
- Uri newUri;
- if (mEntity != null) {
- newUri = provider.insertEntity(mUri, mEntity);
- } else {
- newUri = provider.insert(mUri, values);
- }
+ Uri newUri = provider.insert(mUri, values);
if (newUri == null) {
throw new OperationApplicationException("insert failed");
}
@@ -217,11 +206,7 @@ public class ContentProviderOperation implements Parcelable {
if (mType == TYPE_DELETE) {
numRows = provider.delete(mUri, mSelection, selectionArgs);
} else if (mType == TYPE_UPDATE) {
- if (mEntity != null) {
- numRows = provider.updateEntity(mUri, mEntity);
- } else {
- numRows = provider.update(mUri, values, mSelection, selectionArgs);
- }
+ numRows = provider.update(mUri, values, mSelection, selectionArgs);
} else if (mType == TYPE_COUNT) {
Cursor cursor = provider.query(mUri, COUNT_COLUMNS, mSelection, selectionArgs, null);
try {
@@ -322,7 +307,7 @@ public class ContentProviderOperation implements Parcelable {
*/
private static String backRefToValue(ContentProviderResult[] backRefs, int numBackRefs,
Integer backRefIndex) {
- if (backRefIndex > numBackRefs) {
+ if (backRefIndex >= numBackRefs) {
throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex
+ " but there are only " + numBackRefs + " back refs");
}
@@ -343,7 +328,7 @@ public class ContentProviderOperation implements Parcelable {
public static final Creator<ContentProviderOperation> CREATOR =
new Creator<ContentProviderOperation>() {
public ContentProviderOperation createFromParcel(Parcel source) {
- return new ContentProviderOperation(source, VMStack.getCallingClassLoader2());
+ return new ContentProviderOperation(source);
}
public ContentProviderOperation[] newArray(int size) {
@@ -368,7 +353,6 @@ public class ContentProviderOperation implements Parcelable {
private String mSelection;
private String[] mSelectionArgs;
private ContentValues mValues;
- private Entity mEntity;
private Integer mExpectedCount;
private ContentValues mValuesBackReferences;
private Map<Integer, Integer> mSelectionArgsBackReferences;
@@ -384,14 +368,6 @@ public class ContentProviderOperation implements Parcelable {
/** 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);
}
@@ -414,33 +390,39 @@ public class ContentProviderOperation implements Parcelable {
}
/**
- * 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 {@link #withSelection} will be overwritten.
- * This can only be used with builders of type update, delete, or count query.
+ * Add a ContentValues back reference.
+ * A column value from the back references takes precedence over a value specified in
+ * {@link #withValues}.
+ * This can only be used with builders of type insert or update.
* @return this builder, to allow for chaining.
*/
- public Builder withSelectionBackReferences(Map<Integer, Integer> backReferences) {
- if (mType != TYPE_COUNT && mType != TYPE_UPDATE && mType != TYPE_DELETE) {
+ public Builder withValueBackReference(String key, int previousResult) {
+ if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
throw new IllegalArgumentException(
- "only deletes, updates and counts can have selection back-references");
+ "only inserts and updates can have value back-references");
}
- mSelectionArgsBackReferences = backReferences;
+ if (mValuesBackReferences == null) {
+ mValuesBackReferences = new ContentValues();
+ }
+ mValuesBackReferences.put(key, previousResult);
return this;
}
/**
- * 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.
+ * Add a back references as a selection arg. Any value at that index of the selection arg
+ * 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.
*/
- public Builder withValues(ContentValues values) {
- if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
- throw new IllegalArgumentException("only inserts and updates can have values");
+ public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) {
+ if (mType != TYPE_COUNT && mType != TYPE_UPDATE && mType != TYPE_DELETE) {
+ throw new IllegalArgumentException(
+ "only deletes, updates and counts can have selection back-references");
}
- mValues = values;
+ if (mSelectionArgsBackReferences == null) {
+ mSelectionArgsBackReferences = new HashMap<Integer, Integer>();
+ }
+ mSelectionArgsBackReferences.put(selectionArgIndex, previousResult);
return this;
}
@@ -450,11 +432,11 @@ public class ContentProviderOperation implements Parcelable {
* This can only be used with builders of type insert or update.
* @return this builder, to allow for chaining.
*/
- public Builder withEntity(Entity entity) {
+ public Builder withValues(ContentValues values) {
if (mType != TYPE_INSERT && mType != TYPE_UPDATE) {
- throw new IllegalArgumentException("only inserts and updates can have an entity");
+ throw new IllegalArgumentException("only inserts and updates can have values");
}
- mEntity = entity;
+ mValues = values;
return this;
}
@@ -462,7 +444,7 @@ public class ContentProviderOperation implements Parcelable {
* 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
- * specified by {@link #withSelectionBackReferences}.
+ * specified by {@link #withSelectionBackReference}.
* This can only be used with builders of type update, delete, or count query.
* @return this builder, to allow for chaining.
*/
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
index 34aaa6d..5d188ef 100644
--- a/core/java/android/content/ContentProviderResult.java
+++ b/core/java/android/content/ContentProviderResult.java
@@ -74,4 +74,11 @@ public class ContentProviderResult implements Parcelable {
return new ContentProviderResult[size];
}
};
+
+ public String toString() {
+ if (uri != null) {
+ return "ContentProviderResult(uri=" + uri.toString() + ")";
+ }
+ return "ContentProviderResult(count=" + count + ")";
+ }
} \ No newline at end of file
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f7b52fa..a01c5d1 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -41,6 +41,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
+import java.util.ArrayList;
/**
@@ -567,50 +568,6 @@ 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) {
- throw new IllegalArgumentException("Unknown URL " + uri);
- }
- try {
- return provider.insertEntity(uri, entity);
- } finally {
- provider.release();
- }
- }
-
- /**
- * 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) {
- throw new IllegalArgumentException("Unknown URL " + uri);
- }
- try {
- return provider.updateEntity(uri, entity);
- } finally {
- provider.release();
- }
- }
-
- /**
* 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}.
@@ -627,7 +584,7 @@ public abstract class ContentResolver {
* to communicate with a remote provider.
*/
public ContentProviderResult[] applyBatch(String authority,
- ContentProviderOperation[] operations)
+ ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
ContentProviderClient provider = acquireContentProviderClient(authority);
if (provider == null) {
diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java
index 31ea2cd..325dce5 100644
--- a/core/java/android/content/Entity.java
+++ b/core/java/android/content/Entity.java
@@ -17,10 +17,88 @@
package android.content;
import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+import android.util.Log;
+
+import java.util.ArrayList;
/**
* Objects that pass through the ContentProvider and ContentResolver's methods that deal with
* Entities must implement this abstract base class and thus themselves be Parcelable.
*/
-public abstract class Entity implements Parcelable {
+public final class Entity implements Parcelable {
+ final private ContentValues mValues;
+ final private ArrayList<NamedContentValues> mSubValues;
+
+ public Entity(ContentValues values) {
+ mValues = values;
+ mSubValues = new ArrayList<NamedContentValues>();
+ }
+
+ public ContentValues getEntityValues() {
+ return mValues;
+ }
+
+ public ArrayList<NamedContentValues> getSubValues() {
+ return mSubValues;
+ }
+
+ public void addSubValue(Uri uri, ContentValues values) {
+ mSubValues.add(new Entity.NamedContentValues(uri, values));
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ mValues.writeToParcel(dest, 0);
+ dest.writeInt(mSubValues.size());
+ for (NamedContentValues value : mSubValues) {
+ value.uri.writeToParcel(dest, 0);
+ value.values.writeToParcel(dest, 0);
+ }
+ }
+
+ private Entity(Parcel source) {
+ mValues = ContentValues.CREATOR.createFromParcel(source);
+ final int numValues = source.readInt();
+ mSubValues = new ArrayList<NamedContentValues>(numValues);
+ for (int i = 0; i < numValues; i++) {
+ final Uri uri = Uri.CREATOR.createFromParcel(source);
+ final ContentValues values = ContentValues.CREATOR.createFromParcel(source);
+ mSubValues.add(new NamedContentValues(uri, values));
+ }
+ }
+
+ public static final Creator<Entity> CREATOR = new Creator<Entity>() {
+ public Entity createFromParcel(Parcel source) {
+ return new Entity(source);
+ }
+
+ public Entity[] newArray(int size) {
+ return new Entity[size];
+ }
+ };
+
+ public static class NamedContentValues {
+ public final Uri uri;
+ public final ContentValues values;
+
+ public NamedContentValues(Uri uri, ContentValues values) {
+ this.uri = uri;
+ this.values = values;
+ }
+ }
+
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Entity: ").append(getEntityValues());
+ for (Entity.NamedContentValues namedValue : getSubValues()) {
+ sb.append("\n ").append(namedValue.uri);
+ sb.append("\n -> ").append(namedValue.values);
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java
index 61914cf..5e5f14c 100644
--- a/core/java/android/content/EntityIterator.java
+++ b/core/java/android/content/EntityIterator.java
@@ -18,7 +18,7 @@ package android.content;
import android.os.RemoteException;
-public interface EntityIterator {
+public interface EntityIterator {
/**
* Returns whether there are more elements to iterate, i.e. whether the
* iterator is positioned in front of an element.
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index f82c982..7e5aba5 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -28,6 +28,7 @@ import android.os.IInterface;
import android.os.ParcelFileDescriptor;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* The ipc interface to talk to a content provider.
@@ -60,7 +61,7 @@ public interface IContentProvider extends IInterface {
throws RemoteException, FileNotFoundException;
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException;
- public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations)
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
/* IPC constants */
diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java
index 1887df2..1c478b3 100644
--- a/core/java/android/content/IEntityIterator.java
+++ b/core/java/android/content/IEntityIterator.java
@@ -21,6 +21,7 @@ import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
+import android.os.Parcelable;
import android.util.Log;
/**
@@ -93,7 +94,7 @@ public interface IEntityIterator extends IInterface {
return true;
}
reply.writeNoException();
- reply.writeParcelable(entity, 0);
+ entity.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
}
@@ -149,7 +150,7 @@ public interface IEntityIterator extends IInterface {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0);
_reply.readException();
- return (Entity) _reply.readParcelable(null /* classLoader */);
+ return Entity.CREATOR.createFromParcel(_reply);
} finally {
_reply.recycle();
_data.recycle();
diff --git a/core/java/android/content/SyncAdapterNew.java b/core/java/android/content/SyncAdapterNew.java
new file mode 100644
index 0000000..5b23395
--- /dev/null
+++ b/core/java/android/content/SyncAdapterNew.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2006 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.os.*;
+import android.os.Process;
+import android.accounts.Account;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @hide
+ */
+public abstract class SyncAdapterNew {
+ private static final String TAG = "SyncAdapter";
+ private final Context mContext;
+ private final String mAuthority;
+
+ /** Kernel event log tag. Also listed in data/etc/event-log-tags. */
+ public static final int LOG_SYNC_DETAILS = 2743;
+
+ public SyncAdapterNew(Context context, String authority) {
+ mContext = context;
+ mAuthority = authority;
+ }
+
+ class Transport extends ISyncAdapter.Stub {
+ private final AtomicInteger mNumSyncStarts = new AtomicInteger(0);
+ private volatile Thread mSyncThread;
+
+ public void startSync(ISyncContext syncContext, Account account, Bundle extras) {
+ boolean alreadyInProgress;
+ synchronized (this) {
+ if (mSyncThread == null) {
+ mSyncThread = new Thread(
+ new SyncRunnable(new SyncContext(syncContext), account, extras),
+ "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet());
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ mSyncThread.start();
+ alreadyInProgress = false;
+ } else {
+ alreadyInProgress = true;
+ }
+ }
+
+ if (alreadyInProgress) {
+ try {
+ syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
+ } catch (RemoteException e) {
+ // don't care if the caller is no longer around
+ }
+ }
+ }
+
+ public void cancelSync() {
+ synchronized (this) {
+ if (mSyncThread != null) {
+ mSyncThread.interrupt();
+ }
+ }
+ }
+
+ private class SyncRunnable implements Runnable {
+ private final SyncContext mSyncContext;
+ private final Account mAccount;
+ private final Bundle mExtras;
+
+ private SyncRunnable(SyncContext syncContext, Account account, Bundle extras) {
+ mSyncContext = syncContext;
+ mAccount = account;
+ mExtras = extras;
+ }
+
+ public void run() {
+ if (isCanceled()) {
+ return;
+ }
+
+ SyncResult syncResult = new SyncResult();
+ ContentProviderClient provider = mAuthority != null
+ ? mContext.getContentResolver().acquireContentProviderClient(mAuthority)
+ : null;
+ try {
+ SyncAdapterNew.this.performSync(mAccount, mExtras, provider, syncResult);
+ } finally {
+ if (provider != null) {
+ provider.release();
+ }
+ if (!isCanceled()) {
+ mSyncContext.onFinished(syncResult);
+ }
+ mSyncThread = null;
+ }
+ }
+
+ private boolean isCanceled() {
+ return Thread.currentThread().isInterrupted();
+ }
+ }
+ }
+
+ Transport mTransport = new Transport();
+
+ /**
+ * Get the Transport object.
+ */
+ public final ISyncAdapter getISyncAdapter() {
+ return mTransport;
+ }
+
+ /**
+ * Perform a sync for this account. SyncAdapter-specific parameters may
+ * be specified in extras, which is guaranteed to not be null. Invocations
+ * of this method are guaranteed to be serialized.
+ *
+ * @param account the account that should be synced
+ * @param extras SyncAdapter-specific parameters
+ */
+ public abstract void performSync(Account account, Bundle extras,
+ ContentProviderClient provider, SyncResult syncResult);
+} \ No newline at end of file
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index 4ac0aef..27a02e2 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -166,6 +166,48 @@ public abstract class AbstractWindowedCursor extends AbstractCursor
return mWindow.isBlob(mPos, columnIndex);
}
+ public boolean isString(int columnIndex)
+ {
+ checkPosition();
+
+ synchronized(mUpdatedRows) {
+ if (isFieldUpdated(columnIndex)) {
+ Object object = getUpdatedField(columnIndex);
+ return object == null || object instanceof String;
+ }
+ }
+
+ return mWindow.isString(mPos, columnIndex);
+ }
+
+ public boolean isLong(int columnIndex)
+ {
+ checkPosition();
+
+ synchronized(mUpdatedRows) {
+ if (isFieldUpdated(columnIndex)) {
+ Object object = getUpdatedField(columnIndex);
+ return object != null && (object instanceof Integer || object instanceof Long);
+ }
+ }
+
+ return mWindow.isLong(mPos, columnIndex);
+ }
+
+ public boolean isFloat(int columnIndex)
+ {
+ checkPosition();
+
+ synchronized(mUpdatedRows) {
+ if (isFieldUpdated(columnIndex)) {
+ Object object = getUpdatedField(columnIndex);
+ return object != null && (object instanceof Float || object instanceof Double);
+ }
+ }
+
+ return mWindow.isFloat(mPos, columnIndex);
+ }
+
@Override
protected void checkPosition()
{
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 8e26730..99db81b 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -263,7 +263,58 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
}
}
+ /**
+ * Checks if a field contains a long
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ * @param col the column to read from
+ * @return {@code true} if given field is a long
+ */
+ public boolean isLong(int row, int col) {
+ acquireReference();
+ try {
+ return isInteger_native(row - mStartPos, col);
+ } finally {
+ releaseReference();
+ }
+ }
+
+ /**
+ * Checks if a field contains a float.
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ * @param col the column to read from
+ * @return {@code true} if given field is a float
+ */
+ public boolean isFloat(int row, int col) {
+ acquireReference();
+ try {
+ return isFloat_native(row - mStartPos, col);
+ } finally {
+ releaseReference();
+ }
+ }
+
+ /**
+ * Checks if a field contains either a String or is null.
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ * @param col the column to read from
+ * @return {@code true} if given field is {@code NULL} or a String
+ */
+ public boolean isString(int row, int col) {
+ acquireReference();
+ try {
+ return isString_native(row - mStartPos, col);
+ } finally {
+ releaseReference();
+ }
+ }
+
private native boolean isBlob_native(int row, int col);
+ private native boolean isString_native(int row, int col);
+ private native boolean isInteger_native(int row, int col);
+ private native boolean isFloat_native(int row, int col);
/**
* Returns a String for the given field.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index cc71547..9d48bc78 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -31,6 +31,75 @@ public final class ContactsContract {
/** A content:// style uri to the authority for the contacts provider */
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+ public interface AccountsColumns {
+ /**
+ * The name of this account data
+ * <P>Type: TEXT</P>
+ */
+ public static final String NAME = "name";
+ /**
+ * The name of this account data
+ * <P>Type: TEXT</P>
+ */
+ public static final String TYPE = "type";
+ /**
+ * The name of this account data
+ * <P>Type: TEXT</P>
+ */
+ public static final String DATA1 = "data1";
+
+ /**
+ * The value for this account data
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATA2 = "data2";
+
+ /**
+ * The value for this account data
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATA3 = "data3";
+
+ /**
+ * The value for this account data
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATA4 = "data4";
+
+ /**
+ * The value for this account data
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATA5 = "data5";
+ }
+
+ /**
+ * Constants for the aggregates table, which contains a record per group
+ * of contact representing the same person.
+ */
+ public static final class Accounts implements BaseColumns, AccountsColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Accounts() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "accounts");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of
+ * account data.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contacts_account";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a account
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contacts_account";
+ }
+
public interface AggregatesColumns {
/**
* The display name for the contact.
@@ -136,6 +205,11 @@ public final class ContactsContract {
private Contacts() {}
/**
+ * A reference to the {@link Accounts#_ID} that this data belongs to.
+ */
+ public static final String ACCOUNTS_ID = "accounts_id";
+
+ /**
* A reference to the {@link Aggregates#_ID} that this data belongs to.
*/
public static final String AGGREGATE_ID = "aggregate_id";
@@ -167,6 +241,22 @@ public final class ContactsContract {
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person";
/**
+ * A string that uniquely identifies this contact to its source, which is referred to
+ * by the {@link #ACCOUNTS_ID}
+ */
+ public static final String SOURCE_ID = "sourceid";
+
+ /**
+ * An integer that is updated whenever this contact or its data changes.
+ */
+ public static final String VERSION = "version";
+
+ /**
+ * Set to 1 whenever the version changes
+ */
+ public static final String DIRTY = "dirty";
+
+ /**
* A sub-directory of a single contact that contains all of their {@link Data} rows.
* To access this directory append
*/
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index f19fbbf..91449bc 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -229,7 +229,7 @@ static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column
{
int32_t err;
CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window);
+LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
field_slot_t field;
err = window->read_field_slot(row, column, &field);
@@ -241,6 +241,54 @@ LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window
return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
}
+static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
+}
+
+static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ return field.type == FIELD_TYPE_INTEGER;
+}
+
+static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ return field.type == FIELD_TYPE_FLOAT;
+}
+
static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
{
int32_t err;
@@ -326,11 +374,11 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
return NULL;
}
-
+
jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
if (buffer == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
- return NULL;
+ return NULL;
}
jchar* dst = env->GetCharArrayElements(buffer, NULL);
uint8_t type = field.type;
@@ -338,7 +386,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
jcharArray newArray = NULL;
if (type == FIELD_TYPE_STRING) {
uint32_t size = field.data.buffer.size;
- if (size > 0) {
+ if (size > 0) {
#if WINDOW_STORAGE_UTF8
// Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
@@ -346,7 +394,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
if (strSize > bufferSize || dst == NULL) {
newArray = env->NewCharArray(strSize);
env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
- } else {
+ } else {
memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
}
sizeCopied = strSize;
@@ -359,7 +407,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
}
#endif
- }
+ }
} else if (type == FIELD_TYPE_INTEGER) {
int64_t value;
if (window->getLong(row, column, &value)) {
@@ -628,6 +676,9 @@ static JNINativeMethod sMethods[] =
{"putDouble_native", "(DII)Z", (void *)putDouble_native},
{"freeLastRow_native", "()V", (void *)freeLastRow},
{"putNull_native", "(II)Z", (void *)putNull_native},
+ {"isString_native", "(II)Z", (void *)isString_native},
+ {"isFloat_native", "(II)Z", (void *)isFloat_native},
+ {"isInteger_native", "(II)Z", (void *)isInteger_native},
};
int register_android_database_CursorWindow(JNIEnv * env)
@@ -646,7 +697,7 @@ int register_android_database_CursorWindow(JNIEnv * env)
LOGE("Error locating fields");
return -1;
}
-
+
clazz = env->FindClass("android/database/CharArrayBuffer");
if (clazz == NULL) {
LOGE("Can't find android/database/CharArrayBuffer");