summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-08-06 12:16:55 -0700
committerDianne Hackborn <hackbod@google.com>2010-08-08 18:49:31 -0700
commit23fdaf6fb62a9b5154b2508916a21c678462c5d0 (patch)
tree14b794a1f76f738576bbaa8295141914f5b2b123 /core
parent163935113919a184122b8b3bd672ef08c8df65dc (diff)
downloadframeworks_base-23fdaf6fb62a9b5154b2508916a21c678462c5d0.zip
frameworks_base-23fdaf6fb62a9b5154b2508916a21c678462c5d0.tar.gz
frameworks_base-23fdaf6fb62a9b5154b2508916a21c678462c5d0.tar.bz2
Add new ContentProvider for doing conversions to data streams.
This introduces basic infrastructure that should allow content providers holding complex data to perform on-demand conversion of their data to streams of various types. It is achieved through two new content provider APIs, one to interrogate the possible stream MIME types the provider can return, and the other to request a stream of data in a particular MIME type. Because implementations of this will often need to do on-demand data conversion, there is also a utility intoduced in ContentProvider for subclasses to easily run a function to write data into a pipe that is read by the client. This feature is mostly intended for cut and paste and drag and drop, as the complex data interchange allowing the source and destination to negotiate data types and copy (possible large) data between them. However because it is fundamental facility of ContentProvider, it can be used in other places, such as for more advanced GET_CONTENT data exchanges. An example implementation of this would be in ContactsProvider, which can now provider a data stream when a client opens certain pieces of it data, to return data as flat text, a vcard, or other format. Change-Id: I58627ea4ed359aa7cf2c66274adb18306c209cb2
Diffstat (limited to 'core')
-rw-r--r--core/java/android/content/ClipboardManager.java12
-rw-r--r--core/java/android/content/ClippedData.java227
-rw-r--r--core/java/android/content/ContentProvider.java177
-rw-r--r--core/java/android/content/ContentProviderClient.java43
-rw-r--r--core/java/android/content/ContentProviderNative.java77
-rw-r--r--core/java/android/content/ContentResolver.java153
-rw-r--r--core/java/android/content/IContentProvider.java8
-rw-r--r--core/java/android/content/Intent.java9
-rw-r--r--core/java/android/content/res/AssetFileDescriptor.java8
-rw-r--r--core/java/android/os/AsyncTask.java5
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java19
-rw-r--r--core/java/android/widget/TextView.java53
-rw-r--r--core/jni/android_os_ParcelFileDescriptor.cpp22
13 files changed, 748 insertions, 65 deletions
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 5371fa5..d685cf3 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -34,6 +34,12 @@ import java.util.ArrayList;
* You do not instantiate this class directly; instead, retrieve it through
* {@link android.content.Context#getSystemService}.
*
+ * <p>
+ * The ClipboardManager API itself is very simple: it consists of methods
+ * to atomically get and set the current primary clipboard data. That data
+ * is expressed as a {@link ClippedData} object, which defines the protocol
+ * for data exchange between applications.
+ *
* @see android.content.Context#getSystemService
*/
public class ClipboardManager extends android.text.ClipboardManager {
@@ -152,7 +158,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
public CharSequence getText() {
ClippedData clip = getPrimaryClip();
if (clip != null && clip.getItemCount() > 0) {
- return clip.getItem(0).getText();
+ return clip.getItem(0).coerceToText(mContext);
}
return null;
}
@@ -167,11 +173,11 @@ public class ClipboardManager extends android.text.ClipboardManager {
}
/**
- * Returns true if the clipboard has a primary clip containing text; false otherwise.
+ * @deprecated Use {@link #hasPrimaryClip()} instead.
*/
public boolean hasText() {
try {
- return getService().hasClipboardText();
+ return getService().hasPrimaryClip();
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/content/ClippedData.java b/core/java/android/content/ClippedData.java
index ebb194f..c3f0237 100644
--- a/core/java/android/content/ClippedData.java
+++ b/core/java/android/content/ClippedData.java
@@ -16,12 +16,18 @@
package android.content;
+import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.Log;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.util.ArrayList;
/**
@@ -31,19 +37,88 @@ import java.util.ArrayList;
* each of which can hold one or more representations of an item of data.
* For display to the user, it also has a label and iconic representation.</p>
*
- * <p>The types than an individial item can currently contain are:</p>
- *
- * <ul>
- * <li> Text: a basic string of text. This is actually a CharSequence,
- * so it can be formatted text supported by corresponding Android built-in
- * style spans. (Custom application spans are not supported and will be
- * stripped when transporting through the clipboard.)
- * <li> Intent: an arbitrary Intent object. A typical use is the shortcut
- * to create when pasting a clipped item on to the home screen.
- * <li> Uri: a URI reference. Currently this should only be a content: URI.
- * This representation allows an application to share complex or large clips,
- * by providing a URI to a content provider holding the data.
- * </ul>
+ * <p>Each Item instance can be one of three main classes of data: a simple
+ * CharSequence of text, a single Intent object, or a Uri. See {@link Item}
+ * for more details.
+ *
+ * <a name="ImplementingPaste"></a>
+ * <h3>Implementing Paste or Drop</h3>
+ *
+ * <p>To implement a paste or drop of a ClippedData object into an application,
+ * the application must correctly interpret the data for its use. If the {@link Item}
+ * it contains is simple text or an Intent, there is little to be done: text
+ * can only be interpreted as text, and an Intent will typically be used for
+ * creating shortcuts (such as placing icons on the home screen) or other
+ * actions.
+ *
+ * <p>If all you want is the textual representation of the clipped data, you
+ * can use the convenience method {@link Item#coerceToText Item.coerceToText}.
+ *
+ * <p>More complicated exchanges will be done through URIs, in particular
+ * "content:" URIs. A content URI allows the recipient of a ClippedData item
+ * to interact closely with the ContentProvider holding the data in order to
+ * negotiate the transfer of that data.
+ *
+ * <p>For example, here is the paste function of a simple NotePad application.
+ * When retrieving the data from the clipboard, it can do either two things:
+ * if the clipboard contains a URI reference to an existing note, it copies
+ * the entire structure of the note into a new note; otherwise, it simply
+ * coerces the clip into text and uses that as the new note's contents.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
+ * paste}
+ *
+ * <p>In many cases an application can paste various types of streams of data. For
+ * example, an e-mail application may want to allow the user to paste an image
+ * or other binary data as an attachment. This is accomplished through the
+ * ContentResolver {@link ContentResolver#getStreamTypes(Uri, String)} and
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, android.os.Bundle)}
+ * methods. These allow a client to discover the type(s) of data that a particular
+ * content URI can make available as a stream and retrieve the stream of data.
+ *
+ * <p>For example, the implementation of {@link Item#coerceToText Item.coerceToText}
+ * itself uses this to try to retrieve a URI clip as a stream of text:
+ *
+ * {@sample frameworks/base/core/java/android/content/ClippedData.java coerceToText}
+ *
+ * <a name="ImplementingCopy"></a>
+ * <h3>Implementing Copy or Drag</h3>
+ *
+ * <p>To be the source of a clip, the application must construct a ClippedData
+ * object that any recipient can interpret best for their context. If the clip
+ * is to contain a simple text, Intent, or URI, this is easy: an {@link Item}
+ * containing the appropriate data type can be constructed and used.
+ *
+ * <p>More complicated data types require the implementation of support in
+ * a ContentProvider for describing and generating the data for the recipient.
+ * A common scenario is one where an application places on the clipboard the
+ * content: URI of an object that the user has copied, with the data at that
+ * URI consisting of a complicated structure that only other applications with
+ * direct knowledge of the structure can use.
+ *
+ * <p>For applications that do not have intrinsic knowledge of the data structure,
+ * the content provider holding it can make the data available as an arbitrary
+ * number of types of data streams. This is done by implementing the
+ * ContentProvider {@link ContentProvider#getStreamTypes(Uri, String)} and
+ * {@link ContentProvider#openTypedAssetFile(Uri, String, android.os.Bundle)}
+ * methods.
+ *
+ * <p>Going back to our simple NotePad application, this is the implementation
+ * it may have to convert a single note URI (consisting of a title and the note
+ * text) into a stream of plain text data.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
+ * stream}
+ *
+ * <p>The copy operation in our NotePad application is now just a simple matter
+ * of making a clip containing the URI of the note being copied:
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotesList.java
+ * copy}
+ *
+ * <p>Note if a paste operation needs this clip as text (for example to paste
+ * into an editor), then {@link Item#coerceToText(Context)} will ask the content
+ * provider for the clip URI as text and successfully paste the entire note.
*/
public class ClippedData implements Parcelable {
CharSequence mLabel;
@@ -51,40 +126,166 @@ public class ClippedData implements Parcelable {
final ArrayList<Item> mItems = new ArrayList<Item>();
+ /**
+ * Description of a single item in a ClippedData.
+ *
+ * <p>The types than an individual item can currently contain are:</p>
+ *
+ * <ul>
+ * <li> Text: a basic string of text. This is actually a CharSequence,
+ * so it can be formatted text supported by corresponding Android built-in
+ * style spans. (Custom application spans are not supported and will be
+ * stripped when transporting through the clipboard.)
+ * <li> Intent: an arbitrary Intent object. A typical use is the shortcut
+ * to create when pasting a clipped item on to the home screen.
+ * <li> Uri: a URI reference. This may be any URI (such as an http: URI
+ * representing a bookmark), however it is often a content: URI. Using
+ * content provider references as clips like this allows an application to
+ * share complex or large clips through the standard content provider
+ * facilities.
+ * </ul>
+ */
public static class Item {
CharSequence mText;
Intent mIntent;
Uri mUri;
+ /**
+ * Create an Item consisting of a single block of (possibly styled) text.
+ */
public Item(CharSequence text) {
mText = text;
}
+ /**
+ * Create an Item consisting of an arbitrary Intent.
+ */
public Item(Intent intent) {
mIntent = intent;
}
+ /**
+ * Create an Item consisting of an arbitrary URI.
+ */
public Item(Uri uri) {
mUri = uri;
}
+ /**
+ * Create a complex Item, containing multiple representations of
+ * text, intent, and/or URI.
+ */
public Item(CharSequence text, Intent intent, Uri uri) {
mText = text;
mIntent = intent;
mUri = uri;
}
+ /**
+ * Retrieve the raw text contained in this Item.
+ */
public CharSequence getText() {
return mText;
}
+ /**
+ * Retrieve the raw Intent contained in this Item.
+ */
public Intent getIntent() {
return mIntent;
}
+ /**
+ * Retrieve the raw URI contained in this Item.
+ */
public Uri getUri() {
return mUri;
}
+
+ /**
+ * Turn this item into text, regardless of the type of data it
+ * actually contains.
+ *
+ * <p>The algorithm for deciding what text to return is:
+ * <ul>
+ * <li> If {@link #getText} is non-null, return that.
+ * <li> If {@link #getUri} is non-null, try to retrieve its data
+ * as a text stream from its content provider. If this succeeds, copy
+ * the text into a String and return it. If it is not a content: URI or
+ * the content provider does not supply a text representation, return
+ * the raw URI as a string.
+ * <li> If {@link #getIntent} is non-null, convert that to an intent:
+ * URI and returnit.
+ * <li> Otherwise, return an empty string.
+ * </ul>
+ *
+ * @param context The caller's Context, from which its ContentResolver
+ * and other things can be retrieved.
+ * @return Returns the item's textual representation.
+ */
+//BEGIN_INCLUDE(coerceToText)
+ public CharSequence coerceToText(Context context) {
+ // If this Item has an explicit textual value, simply return that.
+ if (mText != null) {
+ return mText;
+ }
+
+ // If this Item has a URI value, try using that.
+ if (mUri != null) {
+
+ // First see if the URI can be opened as a plain text stream
+ // (of any sub-type). If so, this is the best textual
+ // representation for it.
+ FileInputStream stream = null;
+ try {
+ // Ask for a stream of the desired type.
+ AssetFileDescriptor descr = context.getContentResolver()
+ .openTypedAssetFileDescriptor(mUri, "text/*", null);
+ stream = descr.createInputStream();
+ InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
+
+ // Got it... copy the stream into a local string and return it.
+ StringBuilder builder = new StringBuilder(128);
+ char[] buffer = new char[8192];
+ int len;
+ while ((len=reader.read(buffer)) > 0) {
+ builder.append(buffer, 0, len);
+ }
+ return builder.toString();
+
+ } catch (FileNotFoundException e) {
+ // Unable to open content URI as text... not really an
+ // error, just something to ignore.
+
+ } catch (IOException e) {
+ // Something bad has happened.
+ Log.w("ClippedData", "Failure loading text", e);
+ return e.toString();
+
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ // If we couldn't open the URI as a stream, then the URI itself
+ // probably serves fairly well as a textual representation.
+ return mUri.toString();
+ }
+
+ // Finally, if all we have is an Intent, then we can just turn that
+ // into text. Not the most user-friendly thing, but it's something.
+ if (mIntent != null) {
+ return mIntent.toUri(Intent.URI_INTENT_SCHEME);
+ }
+
+ // Shouldn't get here, but just in case...
+ return "";
+ }
+//END_INCLUDE(coerceToText)
}
/**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a3252ed..1163add 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -28,6 +28,7 @@ import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.database.SQLException;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -36,6 +37,7 @@ import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.ArrayList;
/**
@@ -251,6 +253,18 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.call(method, request, args);
}
+ @Override
+ public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+ return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
+ }
+
+ @Override
+ public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeType, Bundle opts)
+ throws FileNotFoundException {
+ enforceReadPermission(uri);
+ return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts);
+ }
+
private void enforceReadPermission(Uri uri) {
final int uid = Binder.getCallingUid();
if (uid == mMyUid) {
@@ -752,6 +766,164 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
+ * Helper to compare two MIME types, where one may be a pattern.
+ * @param concreteType A fully-specified MIME type.
+ * @param desiredType A desired MIME type that may be a pattern such as *\/*.
+ * @return Returns true if the two MIME types match.
+ */
+ public static boolean compareMimeTypes(String concreteType, String desiredType) {
+ final int typeLength = desiredType.length();
+ if (typeLength == 3 && desiredType.equals("*/*")) {
+ return true;
+ }
+
+ final int slashpos = desiredType.indexOf('/');
+ if (slashpos > 0) {
+ if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
+ if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
+ return true;
+ }
+ } else if (desiredType.equals(concreteType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Called by a client to determine the types of data streams that this
+ * content provider supports for the given URI. The default implementation
+ * returns null, meaning no types. If your content provider stores data
+ * of a particular type, return that MIME type if it matches the given
+ * mimeTypeFilter. If it can perform type conversions, return an array
+ * of all supported MIME types that match mimeTypeFilter.
+ *
+ * @param uri The data in the content provider being queried.
+ * @param mimeTypeFilter The type of data the client desires. May be
+ * a pattern, such as *\/* to retrieve all possible data types.
+ * @returns Returns null if there are no possible data streams for the
+ * given mimeTypeFilter. Otherwise returns an array of all available
+ * concrete MIME types.
+ *
+ * @see #getType(Uri)
+ * @see #openTypedAssetFile(Uri, String, Bundle)
+ * @see #compareMimeTypes(String, String)
+ */
+ public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+ return null;
+ }
+
+ /**
+ * Called by a client to open a read-only stream containing data of a
+ * particular MIME type. This is like {@link #openAssetFile(Uri, String)},
+ * except the file can only be read-only and the content provider may
+ * perform data conversions to generate data of the desired type.
+ *
+ * <p>The default implementation compares the given mimeType against the
+ * result of {@link #getType(Uri)} and, if the match, simple calls
+ * {@link #openAssetFile(Uri, String)}.
+ *
+ * <p>See {@link ClippedData} for examples of the use and implementation
+ * of this method.
+ *
+ * @param uri The data in the content provider being queried.
+ * @param mimeTypeFilter The type of data the client desires. May be
+ * a pattern, such as *\/*, if the caller does not have specific type
+ * requirements; in this case the content provider will pick its best
+ * type matching the pattern.
+ * @param opts Additional options from the client. The definitions of
+ * these are specific to the content provider being called.
+ *
+ * @return Returns a new AssetFileDescriptor from which the client can
+ * read data of the desired type.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the data.
+ * @throws IllegalArgumentException Throws IllegalArgumentException if the
+ * content provider does not support the requested MIME type.
+ *
+ * @see #getStreamTypes(Uri, String)
+ * @see #openAssetFile(Uri, String)
+ * @see #compareMimeTypes(String, String)
+ */
+ public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
+ throws FileNotFoundException {
+ String baseType = getType(uri);
+ if (baseType != null && compareMimeTypes(baseType, mimeTypeFilter)) {
+ return openAssetFile(uri, "r");
+ }
+ throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
+ }
+
+ /**
+ * Interface to write a stream of data to a pipe. Use with
+ * {@link ContentProvider#openPipeHelper}.
+ */
+ public interface PipeDataWriter<T> {
+ /**
+ * Called from a background thread to stream data out to a pipe.
+ * Note that the pipe is blocking, so this thread can block on
+ * writes for an arbitrary amount of time if the client is slow
+ * at reading.
+ *
+ * @param output The pipe where data should be written. This will be
+ * closed for you upon returning from this function.
+ * @param uri The URI whose data is to be written.
+ * @param mimeType The desired type of data to be written.
+ * @param opts Options supplied by caller.
+ * @param args Your own custom arguments.
+ */
+ public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+ Bundle opts, T args);
+ }
+
+ /**
+ * A helper function for implementing {@link #openTypedAssetFile}, for
+ * creating a data pipe and background thread allowing you to stream
+ * generated data back to the client. This function returns a new
+ * ParcelFileDescriptor that should be returned to the caller (the caller
+ * is responsible for closing it).
+ *
+ * @param uri The URI whose data is to be written.
+ * @param mimeType The desired type of data to be written.
+ * @param opts Options supplied by caller.
+ * @param args Your own custom arguments.
+ * @param func Interface implementing the function that will actually
+ * stream the data.
+ * @return Returns a new ParcelFileDescriptor holding the read side of
+ * the pipe. This should be returned to the caller for reading; the caller
+ * is responsible for closing it when done.
+ */
+ public <T> ParcelFileDescriptor openPipeHelper(final Uri uri, final String mimeType,
+ final Bundle opts, final T args, final PipeDataWriter<T> func)
+ throws FileNotFoundException {
+ try {
+ final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+
+ AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() {
+ @Override
+ protected Object doInBackground(Object... params) {
+ func.writeDataToPipe(fds[1], uri, mimeType, opts, args);
+ try {
+ fds[1].close();
+ } catch (IOException e) {
+ Log.w(TAG, "Failure closing pipe", e);
+ }
+ return null;
+ }
+ };
+ task.execute((Object[])null);
+
+ return fds[0];
+ } catch (IOException e) {
+ throw new FileNotFoundException("failure making pipe");
+ }
+ }
+
+ /**
* Returns true if this instance is a temporary content provider.
* @return true if this instance is a temporary content provider
*/
@@ -777,6 +949,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
* @param info Registered information about this content provider
*/
public void attachInfo(Context context, ProviderInfo info) {
+ /*
+ * We may be using AsyncTask from binder threads. Make it init here
+ * so its static handler is on the main thread.
+ */
+ AsyncTask.init();
/*
* Only allow it to be set once, so after the content service gives
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 0858ea5..0540109 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -18,6 +18,7 @@ package android.content;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.ParcelFileDescriptor;
import android.content.res.AssetFileDescriptor;
@@ -43,53 +44,77 @@ public class ContentProviderClient {
mContentResolver = contentResolver;
}
- /** see {@link ContentProvider#query} */
+ /** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder);
}
- /** see {@link ContentProvider#getType} */
+ /** See {@link ContentProvider#getType ContentProvider.getType} */
public String getType(Uri url) throws RemoteException {
return mContentProvider.getType(url);
}
- /** see {@link ContentProvider#insert} */
+ /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+ return mContentProvider.getStreamTypes(url, mimeTypeFilter);
+ }
+
+ /** See {@link ContentProvider#insert ContentProvider.insert} */
public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException {
return mContentProvider.insert(url, initialValues);
}
- /** see {@link ContentProvider#bulkInsert} */
+ /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
return mContentProvider.bulkInsert(url, initialValues);
}
- /** see {@link ContentProvider#delete} */
+ /** See {@link ContentProvider#delete ContentProvider.delete} */
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
return mContentProvider.delete(url, selection, selectionArgs);
}
- /** see {@link ContentProvider#update} */
+ /** See {@link ContentProvider#update ContentProvider.update} */
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
return mContentProvider.update(url, values, selection, selectionArgs);
}
- /** see {@link ContentProvider#openFile} */
+ /**
+ * See {@link ContentProvider#openFile ContentProvider.openFile}. Note that
+ * this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openFileDescriptor
+ * ContentResolver.openFileDescriptor} API instead.
+ */
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
return mContentProvider.openFile(url, mode);
}
- /** see {@link ContentProvider#openAssetFile} */
+ /**
+ * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
+ * Note that this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openAssetFileDescriptor
+ * ContentResolver.openAssetFileDescriptor} API instead.
+ */
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
return mContentProvider.openAssetFile(url, mode);
}
- /** see {@link ContentProvider#applyBatch} */
+ /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+ String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException {
+ return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
+ }
+
+ /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
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 fdb3d20..d1ab8d5 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -257,6 +257,38 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
reply.writeBundle(responseBundle);
return true;
}
+
+ case GET_STREAM_TYPES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri url = Uri.CREATOR.createFromParcel(data);
+ String mimeTypeFilter = data.readString();
+ String[] types = getStreamTypes(url, mimeTypeFilter);
+ reply.writeNoException();
+ reply.writeStringArray(types);
+
+ return true;
+ }
+
+ case OPEN_TYPED_ASSET_FILE_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri url = Uri.CREATOR.createFromParcel(data);
+ String mimeType = data.readString();
+ Bundle opts = data.readBundle();
+
+ AssetFileDescriptor fd;
+ fd = openTypedAssetFile(url, mimeType, opts);
+ reply.writeNoException();
+ if (fd != null) {
+ reply.writeInt(1);
+ fd.writeToParcel(reply,
+ Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ reply.writeInt(0);
+ }
+ return true;
+ }
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -568,5 +600,50 @@ final class ContentProviderProxy implements IContentProvider
return bundle;
}
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ url.writeToParcel(data, 0);
+ data.writeString(mimeTypeFilter);
+
+ mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ String[] out = reply.createStringArray();
+
+ data.recycle();
+ reply.recycle();
+
+ return out;
+ }
+
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ url.writeToParcel(data, 0);
+ data.writeString(mimeType);
+ data.writeBundle(opts);
+
+ mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
+ int has = reply.readInt();
+ AssetFileDescriptor fd = has != 0
+ ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null;
+
+ data.recycle();
+ reply.recycle();
+
+ return fd;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2ea0df96..22feb9a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -186,8 +186,7 @@ public abstract class ContentResolver {
* using the content:// scheme.
* @return A MIME type for the content, or null if the URL is invalid or the type is unknown
*/
- public final String getType(Uri url)
- {
+ public final String getType(Uri url) {
IContentProvider provider = acquireProvider(url);
if (provider == null) {
return null;
@@ -204,6 +203,39 @@ public abstract class ContentResolver {
}
/**
+ * Query for the possible MIME types for the representations the given
+ * content URL can be returned when opened as as stream with
+ * {@link #openTypedAssetFileDescriptor}. Note that the types here are
+ * not necessarily a superset of the type returned by {@link #getType} --
+ * many content providers can not return a raw stream for the structured
+ * data that they contain.
+ *
+ * @param url A Uri identifying content (either a list or specific type),
+ * using the content:// scheme.
+ * @param mimeTypeFilter The desired MIME type. This may be a pattern,
+ * such as *\/*, to query for all available MIME types that match the
+ * pattern.
+ * @return Returns an array of MIME type strings for all availablle
+ * data streams that match the given mimeTypeFilter. If there are none,
+ * null is returned.
+ */
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
+ IContentProvider provider = acquireProvider(url);
+ if (provider == null) {
+ return null;
+ }
+ try {
+ return provider.getStreamTypes(url, mimeTypeFilter);
+ } catch (RemoteException e) {
+ return null;
+ } catch (java.lang.Exception e) {
+ return null;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
* <p>
* Query the given URI, returning a {@link Cursor} over the result set.
* </p>
@@ -349,7 +381,7 @@ public abstract class ContentResolver {
}
/**
- * Open a raw file descriptor to access data under a "content:" URI. This
+ * Open a raw file descriptor to access data under a URI. This
* is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
* underlying {@link ContentProvider#openFile}
* ContentProvider.openFile()} method, so will <em>not</em> work with
@@ -399,7 +431,7 @@ public abstract class ContentResolver {
}
/**
- * Open a raw file descriptor to access data under a "content:" URI. This
+ * Open a raw file descriptor to access data under a URI. This
* interacts with the underlying {@link ContentProvider#openAssetFile}
* method of the provider associated with the given URI, to retrieve any file stored there.
*
@@ -433,6 +465,11 @@ public abstract class ContentResolver {
* </li>
* </ul>
*
+ * <p>Note that if this function is called for read-only input (mode is "r")
+ * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
+ * for you with a MIME type of "*\/*". This allows such callers to benefit
+ * from any built-in data conversion that a provider implements.
+ *
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
* ContentProvider.openAssetFile}.
@@ -459,29 +496,97 @@ public abstract class ContentResolver {
new File(uri.getPath()), modeToMode(uri, mode));
return new AssetFileDescriptor(pfd, 0, -1);
} else {
- IContentProvider provider = acquireProvider(uri);
- if (provider == null) {
- throw new FileNotFoundException("No content provider: " + uri);
- }
- try {
- AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
- if(fd == null) {
- releaseProvider(provider);
- return null;
+ if ("r".equals(mode)) {
+ return openTypedAssetFileDescriptor(uri, "*/*", null);
+ } else {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new FileNotFoundException("No content provider: " + uri);
}
- ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
- fd.getParcelFileDescriptor(), provider);
- return new AssetFileDescriptor(pfd, fd.getStartOffset(),
- fd.getDeclaredLength());
- } catch (RemoteException e) {
- releaseProvider(provider);
- throw new FileNotFoundException("Dead content provider: " + uri);
- } catch (FileNotFoundException e) {
+ try {
+ AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
+ if(fd == null) {
+ releaseProvider(provider);
+ return null;
+ }
+ ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+ fd.getParcelFileDescriptor(), provider);
+
+ // Success! Don't release the provider when exiting, let
+ // ParcelFileDescriptorInner do that when it is closed.
+ provider = null;
+
+ return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+ fd.getDeclaredLength());
+ } catch (RemoteException e) {
+ throw new FileNotFoundException("Dead content provider: " + uri);
+ } catch (FileNotFoundException e) {
+ throw e;
+ } finally {
+ if (provider != null) {
+ releaseProvider(provider);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Open a raw file descriptor to access (potentially type transformed)
+ * data from a "content:" URI. This interacts with the underlying
+ * {@link ContentProvider#openTypedAssetFile} method of the provider
+ * associated with the given URI, to retrieve retrieve any appropriate
+ * data stream for the data stored there.
+ *
+ * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
+ * with "content:" URIs, because content providers are the only facility
+ * with an associated MIME type to ensure that the returned data stream
+ * is of the desired type.
+ *
+ * <p>All text/* streams are encoded in UTF-8.
+ *
+ * @param uri The desired URI to open.
+ * @param mimeType The desired MIME type of the returned data. This can
+ * be a pattern such as *\/*, which will allow the content provider to
+ * select a type, though there is no way for you to determine what type
+ * it is returning.
+ * @param opts Additional provider-dependent options.
+ * @return Returns a new ParcelFileDescriptor from which you can read the
+ * data stream from the provider. Note that this may be a pipe, meaning
+ * you can't seek in it. The only seek you should do is if the
+ * AssetFileDescriptor contains an offset, to move to that offset before
+ * reading. You own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException of no
+ * data of the desired type exists under the URI.
+ */
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+ String mimeType, Bundle opts) throws FileNotFoundException {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new FileNotFoundException("No content provider: " + uri);
+ }
+ try {
+ AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
+ if (fd == null) {
releaseProvider(provider);
- throw e;
- } catch (RuntimeException e) {
+ return null;
+ }
+ ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+ fd.getParcelFileDescriptor(), provider);
+
+ // Success! Don't release the provider when exiting, let
+ // ParcelFileDescriptorInner do that when it is closed.
+ provider = null;
+
+ return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+ fd.getDeclaredLength());
+ } catch (RemoteException e) {
+ throw new FileNotFoundException("Dead content provider: " + uri);
+ } catch (FileNotFoundException e) {
+ throw e;
+ } finally {
+ if (provider != null) {
releaseProvider(provider);
- throw e;
}
}
}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 67e7581..8f122ce 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -59,6 +59,7 @@ public interface IContentProvider extends IInterface {
throws RemoteException, FileNotFoundException;
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
+
/**
* @hide -- until interface has proven itself
*
@@ -71,6 +72,11 @@ public interface IContentProvider extends IInterface {
*/
public Bundle call(String method, String request, Bundle args) throws RemoteException;
+ // Data interchange.
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException;
+
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
@@ -84,4 +90,6 @@ public interface IContentProvider extends IInterface {
static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
+ static final int GET_STREAM_TYPES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 21;
+ static final int OPEN_TYPED_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 22;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2acc4a0..58174fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -985,6 +985,15 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_INSERT = "android.intent.action.INSERT";
/**
+ * Activity Action: Create a new item in the given container, initializing it
+ * from the current contents of the clipboard.
+ * <p>Input: {@link #getData} is URI of the directory (vnd.android.cursor.dir/*)
+ * in which to place the data.
+ * <p>Output: URI of the new data that was created.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PASTE = "android.intent.action.PASTE";
+ /**
* Activity Action: Delete the given data from its container.
* <p>Input: {@link #getData} is URI of data to be deleted.
* <p>Output: nothing.
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index a37e4e8..ccb8605 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -129,8 +129,12 @@ public class AssetFileDescriptor implements Parcelable {
/**
* Checks whether this file descriptor is for a memory file.
*/
- private boolean isMemoryFile() throws IOException {
- return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
+ private boolean isMemoryFile() {
+ try {
+ return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
+ } catch (IOException e) {
+ return false;
+ }
}
/**
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 832ce84..aadacab 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -175,6 +175,11 @@ public abstract class AsyncTask<Params, Progress, Result> {
FINISHED,
}
+ /** @hide Used to force static handler to be created. */
+ public static void init() {
+ sHandler.getLooper();
+ }
+
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 9d213b3..d853f13 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -134,6 +134,25 @@ public class ParcelFileDescriptor implements Parcelable {
private static native FileDescriptor getFileDescriptorFromSocket(Socket socket);
/**
+ * Create two ParcelFileDescriptors structured as a data pipe. The first
+ * ParcelFileDescriptor in the returned array is the read side; the second
+ * is the write side.
+ */
+ public static ParcelFileDescriptor[] createPipe() throws IOException {
+ FileDescriptor[] fds = new FileDescriptor[2];
+ int res = createPipeNative(fds);
+ if (res == 0) {
+ ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
+ pfds[0] = new ParcelFileDescriptor(fds[0]);
+ pfds[1] = new ParcelFileDescriptor(fds[1]);
+ return pfds;
+ }
+ throw new IOException("Unable to create pipe: errno=" + -res);
+ }
+
+ private static native int createPipeNative(FileDescriptor[] outFds);
+
+ /**
* Retrieve the actual FileDescriptor associated with this object.
*
* @return Returns the FileDescriptor associated with this object.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 24f14dc..f3f68f9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,7 +21,9 @@ import com.android.internal.widget.EditableInputConnection;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.ClippedData;
import android.content.Context;
+import android.content.ClipboardManager;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -34,6 +36,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -42,7 +45,6 @@ import android.os.Parcelable;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.text.BoringLayout;
-import android.text.ClipboardManager;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.GetChars;
@@ -7061,7 +7063,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
getSelectionStart() >= 0 &&
getSelectionEnd() >= 0 &&
((ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE)).
- hasText());
+ hasPrimaryClip());
}
/**
@@ -7270,7 +7272,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int min = Math.max(0, Math.min(selStart, selEnd));
int max = Math.max(0, Math.max(selStart, selEnd));
- ClipboardManager clip = (ClipboardManager)getContext()
+ ClipboardManager clipboard = (ClipboardManager)getContext()
.getSystemService(Context.CLIPBOARD_SERVICE);
switch (id) {
@@ -7278,8 +7280,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
- if (urls.length == 1) {
- clip.setText(urls[0].getURL());
+ if (urls.length >= 1) {
+ ClippedData clip = null;
+ for (int i=0; i<urls.length; i++) {
+ Uri uri = Uri.parse(urls[0].getURL());
+ ClippedData.Item item = new ClippedData.Item(uri);
+ if (clip == null) {
+ clip = new ClippedData(null, null, item);
+ } else {
+ clip.addItem(item);
+ }
+ }
+ clipboard.setPrimaryClip(clip);
}
return true;
@@ -7490,8 +7502,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return true;
}
- ClipboardManager clip = (ClipboardManager) getContext().
- getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipboardManager clipboard = (ClipboardManager) getContext().
+ getSystemService(Context.CLIPBOARD_SERVICE);
int min = 0;
int max = mText.length();
@@ -7506,23 +7518,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
switch (item.getItemId()) {
case ID_PASTE:
- CharSequence paste = clip.getText();
-
- if (paste != null) {
- Selection.setSelection((Spannable) mText, max);
- ((Editable) mText).replace(min, max, paste);
+ ClippedData clip = clipboard.getPrimaryClip();
+ if (clip != null) {
+ boolean didfirst = false;
+ for (int i=0; i<clip.getItemCount(); i++) {
+ CharSequence paste = clip.getItem(i).coerceToText(getContext());
+ if (paste != null) {
+ if (!didfirst) {
+ Selection.setSelection((Spannable) mText, max);
+ ((Editable) mText).replace(min, max, paste);
+ } else {
+ ((Editable) mText).insert(getSelectionEnd(), "\n");
+ ((Editable) mText).insert(getSelectionEnd(), paste);
+ }
+ }
+ }
finishSelectionActionMode();
}
+
return true;
case ID_CUT:
- clip.setText(mTransformed.subSequence(min, max));
+ clipboard.setPrimaryClip(new ClippedData(null, null,
+ new ClippedData.Item(mTransformed.subSequence(min, max))));
((Editable) mText).delete(min, max);
finishSelectionActionMode();
return true;
case ID_COPY:
- clip.setText(mTransformed.subSequence(min, max));
+ clipboard.setPrimaryClip(new ClippedData(null, null,
+ new ClippedData.Item(mTransformed.subSequence(min, max))));
finishSelectionActionMode();
return true;
}
diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp
index 848a57a..eceef1c 100644
--- a/core/jni/android_os_ParcelFileDescriptor.cpp
+++ b/core/jni/android_os_ParcelFileDescriptor.cpp
@@ -66,6 +66,26 @@ static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromSocket(JNIEn
return fileDescriptorClone;
}
+static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
+ jobject clazz, jobjectArray outFds)
+{
+ int fds[2];
+ if (pipe(fds) < 0) {
+ return -errno;
+ }
+
+ for (int i=0; i<2; i++) {
+ jobject fdObj = env->NewObject(gFileDescriptorOffsets.mClass,
+ gFileDescriptorOffsets.mConstructor);
+ if (fdObj != NULL) {
+ env->SetIntField(fdObj, gFileDescriptorOffsets.mDescriptor, fds[i]);
+ }
+ env->SetObjectArrayElement(outFds, i, fdObj);
+ }
+
+ return 0;
+}
+
static jint getFd(JNIEnv* env, jobject clazz)
{
jobject descriptor = env->GetObjectField(clazz, gParcelFileDescriptorOffsets.mFileDescriptor);
@@ -109,6 +129,8 @@ static jlong android_os_ParcelFileDescriptor_seekTo(JNIEnv* env,
static const JNINativeMethod gParcelFileDescriptorMethods[] = {
{"getFileDescriptorFromSocket", "(Ljava/net/Socket;)Ljava/io/FileDescriptor;",
(void*)android_os_ParcelFileDescriptor_getFileDescriptorFromSocket},
+ {"createPipeNative", "([Ljava/io/FileDescriptor;)I",
+ (void*)android_os_ParcelFileDescriptor_createPipeNative},
{"getStatSize", "()J",
(void*)android_os_ParcelFileDescriptor_getStatSize},
{"seekTo", "(J)J",