Fix compatibility issues with Android clipboards
* java/org/gnu/emacs/EmacsClipboard.java (getClipboardData): Return an AssetFileDescriptor. * java/org/gnu/emacs/EmacsContextMenu.java (onMenuItemClick): Typo corrections in commentary. * java/org/gnu/emacs/EmacsOpenActivity.java (onCreate): Raise minimum version on which to read file descriptors from ParcelFileDescriptor objects to Honeycomb. * java/org/gnu/emacs/EmacsSdk11Clipboard.java (getClipboardData): Return the asset file descriptor. * java/org/gnu/emacs/EmacsSdk8Clipboard.java (getClipboardData): Adjust return type to match. * src/android.h (struct android_parcel_file_descriptor_class): Move from androidselect.c. * src/androidselect.c (fd_class): Export function. (android_init_emacs_clipboard): Adjust signature of getClipboardData. (android_init_asset_file_descriptor, close_asset_fd) (extract_fd_offsets): New functions. (Fandroid_get_clipboard_data): Extract file descriptor and offset from the AssetFileDescriptor here, rather than in getClipboardData. (init_androidselect): Call android_init_asset_file_descriptor. * src/androidvfs.c (android_init_fd_class): Export and enable calling this function more than once.
This commit is contained in:
parent
294335b230
commit
2451456695
8 changed files with 251 additions and 114 deletions
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.gnu.emacs;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.os.Build;
|
||||
|
||||
/* This class provides helper code for accessing the clipboard,
|
||||
|
@ -32,7 +33,7 @@ public abstract class EmacsClipboard
|
|||
public abstract byte[] getClipboard ();
|
||||
|
||||
public abstract byte[][] getClipboardTargets ();
|
||||
public abstract long[] getClipboardData (byte[] target);
|
||||
public abstract AssetFileDescriptor getClipboardData (byte[] target);
|
||||
|
||||
/* Create the correct kind of clipboard for this system. */
|
||||
|
||||
|
|
|
@ -108,8 +108,8 @@ private static final class Item implements MenuItem.OnMenuItemClickListener
|
|||
will normally confuse Emacs into thinking that the
|
||||
context menu has been dismissed. Wrong!
|
||||
|
||||
Setting this flag makes EmacsActivity to only handle
|
||||
SubMenuBuilder being closed, which always means the menu
|
||||
Setting this flag prompts EmacsActivity to only handle
|
||||
SubMenuBuilders being closed, which always means the menu
|
||||
has actually been dismissed.
|
||||
|
||||
However, these extraneous events aren't sent on devices
|
||||
|
|
|
@ -19,29 +19,23 @@
|
|||
|
||||
package org.gnu.emacs;
|
||||
|
||||
/* This class makes the Emacs server work reasonably on Android.
|
||||
/* Opening external documents on Android.
|
||||
|
||||
There is no way to make the Unix socket publicly available on
|
||||
Android.
|
||||
This activity is registered as an application capable of opening text
|
||||
files and files in several other formats that Emacs understands, and
|
||||
assumes responsibility for deriving file names from the files
|
||||
provided to `onCreate', potentially copying them to temporary
|
||||
directories in the process, and invoking `emacsclient' with suitable
|
||||
arguments to open the same. In this respect, it fills the role of
|
||||
`etc/emacs.desktop' on XDG systems.
|
||||
|
||||
Instead, this activity tries to connect to the Emacs server, to
|
||||
make it open files the system asks Emacs to open, and to emulate
|
||||
some reasonable behavior when Emacs has not yet started.
|
||||
It is also registered as a handler for mailto URIs, in which capacity
|
||||
it constructs invocations of `emacsclient' so as to start
|
||||
`message-mailto' with their contents and attachments, much like
|
||||
`etc/emacs-mail.desktop'.
|
||||
|
||||
First, Emacs registers itself as an application that can open text
|
||||
and image files.
|
||||
|
||||
Then, when the user is asked to open a file and selects ``Emacs''
|
||||
as the application that will open the file, the system pops up a
|
||||
window, this activity, and calls the `onCreate' function.
|
||||
|
||||
`onCreate' then tries very to find the file name of the file that
|
||||
was selected, and give it to emacsclient.
|
||||
|
||||
If emacsclient successfully opens the file, then this activity
|
||||
starts EmacsActivity (to bring it on to the screen); otherwise, it
|
||||
displays the output of emacsclient or any error message that occurs
|
||||
and exits. */
|
||||
As with all other activities, it is registered in the package
|
||||
manifest file. */
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Activity;
|
||||
|
@ -628,11 +622,12 @@ else if (scheme != null
|
|||
|
||||
if (scheme.equals ("content")
|
||||
/* Retrieving the native file descriptor of a
|
||||
ParcelFileDescriptor requires Honeycomb, and
|
||||
ParcelFileDescriptor requires Honeycomb MR1, and
|
||||
proceeding without this capability is pointless on
|
||||
systems before KitKat, since Emacs doesn't support
|
||||
opening content files on those. */
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
||||
&& (Build.VERSION.SDK_INT
|
||||
>= Build.VERSION_CODES.HONEYCOMB_MR1))
|
||||
{
|
||||
/* This is one of the annoying Android ``content''
|
||||
URIs. Most of the time, there is actually an
|
||||
|
|
|
@ -207,8 +207,9 @@ public final class EmacsSdk11Clipboard extends EmacsClipboard
|
|||
/* Return the clipboard data for the given target, or NULL if it
|
||||
does not exist.
|
||||
|
||||
Value is normally an array of three longs: the file descriptor,
|
||||
the start offset of the data, and its length; length may be
|
||||
Value is normally an asset file descriptor, which in turn holds
|
||||
three important values: the file descriptor, the start offset of
|
||||
the data, and its length; length may be
|
||||
AssetFileDescriptor.UNKNOWN_LENGTH, meaning that the data extends
|
||||
from that offset to the end of the file.
|
||||
|
||||
|
@ -217,15 +218,13 @@ public final class EmacsSdk11Clipboard extends EmacsClipboard
|
|||
solely of a URI. */
|
||||
|
||||
@Override
|
||||
public long[]
|
||||
public AssetFileDescriptor
|
||||
getClipboardData (byte[] target)
|
||||
{
|
||||
ClipData data;
|
||||
String mimeType;
|
||||
int fd;
|
||||
AssetFileDescriptor assetFd;
|
||||
Uri uri;
|
||||
long[] value;
|
||||
|
||||
/* Decode the target given by Emacs. */
|
||||
try
|
||||
|
@ -245,8 +244,6 @@ public final class EmacsSdk11Clipboard extends EmacsClipboard
|
|||
if (data == null || data.getItemCount () < 1)
|
||||
return null;
|
||||
|
||||
fd = -1;
|
||||
|
||||
try
|
||||
{
|
||||
uri = data.getItemAt (0).getUri ();
|
||||
|
@ -257,52 +254,15 @@ public final class EmacsSdk11Clipboard extends EmacsClipboard
|
|||
/* Now open the file descriptor. */
|
||||
assetFd = resolver.openTypedAssetFileDescriptor (uri, mimeType,
|
||||
null);
|
||||
|
||||
/* Duplicate the file descriptor. */
|
||||
fd = assetFd.getParcelFileDescriptor ().getFd ();
|
||||
fd = EmacsNative.dup (fd);
|
||||
|
||||
/* Return the relevant information. */
|
||||
value = new long[] { fd, assetFd.getStartOffset (),
|
||||
assetFd.getLength (), };
|
||||
|
||||
/* Close the original offset. */
|
||||
assetFd.close ();
|
||||
return assetFd;
|
||||
}
|
||||
catch (SecurityException e)
|
||||
{
|
||||
/* Guarantee a file descriptor duplicated or detached is
|
||||
ultimately closed if an error arises. */
|
||||
|
||||
if (fd != -1)
|
||||
EmacsNative.close (fd);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
/* Guarantee a file descriptor duplicated or detached is
|
||||
ultimately closed if an error arises. */
|
||||
|
||||
if (fd != -1)
|
||||
EmacsNative.close (fd);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
/* Guarantee a file descriptor duplicated or detached is
|
||||
ultimately closed if an error arises. */
|
||||
|
||||
if (fd != -1)
|
||||
EmacsNative.close (fd);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Don't return value if the file descriptor couldn't be
|
||||
created. */
|
||||
|
||||
return fd != -1 ? value : null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
import android.text.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
@ -129,9 +131,10 @@ public final class EmacsSdk8Clipboard extends EmacsClipboard
|
|||
/* Return the clipboard data for the given target, or NULL if it
|
||||
does not exist.
|
||||
|
||||
Value is normally an array of three longs: the file descriptor,
|
||||
the start offset of the data, and its length; length may be
|
||||
AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
|
||||
Value is normally an asset file descriptor, which in turn holds
|
||||
three important values: the file descriptor, the start offset of
|
||||
the data, and its length; length may be
|
||||
AssetFileDescriptor.UNKNOWN_LENGTH, meaning that the data extends
|
||||
from that offset to the end of the file.
|
||||
|
||||
Do not use this function to open text targets; use `getClipboard'
|
||||
|
@ -139,7 +142,7 @@ public final class EmacsSdk8Clipboard extends EmacsClipboard
|
|||
solely of a URI. */
|
||||
|
||||
@Override
|
||||
public long[]
|
||||
public AssetFileDescriptor
|
||||
getClipboardData (byte[] target)
|
||||
{
|
||||
return null;
|
||||
|
|
|
@ -53,6 +53,22 @@ extern char *android_user_full_name (struct passwd *);
|
|||
|
||||
|
||||
|
||||
/* Structure describing the android.os.ParcelFileDescriptor class used
|
||||
to wrap file descriptors sent over IPC. */
|
||||
|
||||
struct android_parcel_file_descriptor_class
|
||||
{
|
||||
jclass class;
|
||||
jmethodID close;
|
||||
jmethodID get_fd;
|
||||
jmethodID detach_fd;
|
||||
};
|
||||
|
||||
/* The ParcelFileDescriptor class. */
|
||||
extern struct android_parcel_file_descriptor_class fd_class;
|
||||
|
||||
extern void android_init_fd_class (JNIEnv *);
|
||||
|
||||
/* File I/O operations. Many of these are defined in
|
||||
androidvfs.c. */
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
#include <assert.h>
|
||||
#include <minmax.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <boot-time.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -100,7 +101,7 @@ android_init_emacs_clipboard (void)
|
|||
FIND_METHOD (get_clipboard_targets, "getClipboardTargets",
|
||||
"()[[B");
|
||||
FIND_METHOD (get_clipboard_data, "getClipboardData",
|
||||
"([B)[J");
|
||||
"([B)Landroid/content/res/AssetFileDescriptor;");
|
||||
|
||||
clipboard_class.make_clipboard
|
||||
= (*android_java_env)->GetStaticMethodID (android_java_env,
|
||||
|
@ -340,6 +341,62 @@ data type available from the clipboard. */)
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct android_asset_file_descriptor
|
||||
{
|
||||
jclass class;
|
||||
jmethodID close;
|
||||
jmethodID get_length;
|
||||
jmethodID get_start_offset;
|
||||
jmethodID get_file_descriptor;
|
||||
jmethodID get_parcel_file_descriptor;
|
||||
jmethodID get_fd;
|
||||
};
|
||||
|
||||
/* Methods associated with the AssetFileDescriptor class. */
|
||||
static struct android_asset_file_descriptor asset_fd_class;
|
||||
|
||||
/* Initialize virtual function IDs and class pointers in connection with
|
||||
the AssetFileDescriptor class. */
|
||||
|
||||
static void
|
||||
android_init_asset_file_descriptor (void)
|
||||
{
|
||||
jclass old;
|
||||
|
||||
asset_fd_class.class
|
||||
= (*android_java_env)->FindClass (android_java_env,
|
||||
"android/content/res/"
|
||||
"AssetFileDescriptor");
|
||||
eassert (asset_fd_class.class);
|
||||
|
||||
old = asset_fd_class.class;
|
||||
asset_fd_class.class
|
||||
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
|
||||
old);
|
||||
ANDROID_DELETE_LOCAL_REF (old);
|
||||
|
||||
if (!asset_fd_class.class)
|
||||
emacs_abort ();
|
||||
|
||||
#define FIND_METHOD(c_name, name, signature) \
|
||||
asset_fd_class.c_name \
|
||||
= (*android_java_env)->GetMethodID (android_java_env, \
|
||||
asset_fd_class.class, \
|
||||
name, signature); \
|
||||
eassert (asset_fd_class.c_name);
|
||||
|
||||
FIND_METHOD (close, "close", "()V");
|
||||
FIND_METHOD (get_length, "getLength", "()J");
|
||||
FIND_METHOD (get_start_offset, "getStartOffset", "()J");
|
||||
FIND_METHOD (get_file_descriptor, "getFileDescriptor",
|
||||
"()Ljava/io/FileDescriptor;");
|
||||
FIND_METHOD (get_parcel_file_descriptor, "getParcelFileDescriptor",
|
||||
"()Landroid/os/ParcelFileDescriptor;");
|
||||
#undef FIND_METHOD
|
||||
}
|
||||
|
||||
/* Free the memory inside PTR, a pointer to a char pointer. */
|
||||
|
||||
static void
|
||||
|
@ -348,6 +405,125 @@ android_xfree_inside (void *ptr)
|
|||
xfree (*(char **) ptr);
|
||||
}
|
||||
|
||||
/* Close the referent of, then delete, the local reference to an asset
|
||||
file descriptor referenced by AFD. */
|
||||
|
||||
static void
|
||||
close_asset_fd (void *afd)
|
||||
{
|
||||
jobject *afd_1;
|
||||
|
||||
afd_1 = afd;
|
||||
(*android_java_env)->CallVoidMethod (android_java_env, *afd_1,
|
||||
asset_fd_class.close);
|
||||
(*android_java_env)->ExceptionClear (android_java_env);
|
||||
ANDROID_DELETE_LOCAL_REF (*afd_1);
|
||||
}
|
||||
|
||||
/* Return the offset, file descriptor and length of the data contained
|
||||
in the asset file descriptor AFD, in *FD, *OFFSET, and *LENGTH.
|
||||
Value is 0 upon success, 1 otherwise. */
|
||||
|
||||
static int
|
||||
extract_fd_offsets (jobject afd, int *fd, jlong *offset, jlong *length)
|
||||
{
|
||||
jobject java_fd;
|
||||
void *handle;
|
||||
#if __ANDROID_API__ <= 11
|
||||
static int (*jniGetFDFromFileDescriptor) (JNIEnv *, jobject);
|
||||
#endif /* __ANDROID_API__ <= 11 */
|
||||
static int (*AFileDescriptor_getFd) (JNIEnv *, jobject);;
|
||||
jmethodID method;
|
||||
|
||||
method = asset_fd_class.get_start_offset;
|
||||
*offset = (*android_java_env)->CallLongMethod (android_java_env,
|
||||
afd, method);
|
||||
android_exception_check ();
|
||||
method = asset_fd_class.get_length;
|
||||
*length = (*android_java_env)->CallLongMethod (android_java_env,
|
||||
afd, method);
|
||||
android_exception_check ();
|
||||
|
||||
#if __ANDROID_API__ <= 11
|
||||
if (android_get_current_api_level () <= 11)
|
||||
{
|
||||
/* Load libnativehelper and link to a private interface that is
|
||||
the only means of retrieving the file descriptor from an asset
|
||||
file descriptor on these systems. */
|
||||
|
||||
if (!jniGetFDFromFileDescriptor)
|
||||
{
|
||||
handle = dlopen ("libnativehelper.so",
|
||||
RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (!handle)
|
||||
goto failure;
|
||||
jniGetFdFromFileDescriptor = dlsym (handle,
|
||||
"jniGetFDFromFileDescriptor");
|
||||
if (!jniGetFdFromFileDescriptor)
|
||||
goto failure;
|
||||
}
|
||||
|
||||
method = asset_fd_class.get_file_descriptor;
|
||||
java_fd = (*android_java_env)->CallObjectMethod (android_java_env,
|
||||
afd, method);
|
||||
android_exception_check ();
|
||||
*fd = (*jniGetFDFromFileDescriptor) (android_java_env, java_fd);
|
||||
ANDROID_DELETE_LOCAL_REF (java_fd);
|
||||
|
||||
if (*fd >= 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
#endif /* __ANDROID_API__ <= 11 */
|
||||
#if __ANDROID_API__ <= 30
|
||||
if (android_get_current_api_level () <= 30)
|
||||
{
|
||||
/* Convert this AssetFileDescriptor into a ParcelFileDescriptor,
|
||||
whose getFd method will return its native file descriptor. */
|
||||
method = asset_fd_class.get_parcel_file_descriptor;
|
||||
java_fd = (*android_java_env)->CallObjectMethod (android_java_env,
|
||||
afd, method);
|
||||
android_exception_check ();
|
||||
|
||||
/* Initialize fd_class if not already complete. */
|
||||
android_init_fd_class (android_java_env);
|
||||
*fd = (*android_java_env)->CallIntMethod (android_java_env,
|
||||
java_fd,
|
||||
fd_class.get_fd);
|
||||
if (*fd >= 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
#endif /* __ANDROID_API__ <= 30 */
|
||||
{
|
||||
/* Load libnativehelper (now a public interface) and link to
|
||||
AFileDescriptor_getFd. */
|
||||
if (!AFileDescriptor_getFd)
|
||||
{
|
||||
handle = dlopen ("libnativehelper.so",
|
||||
RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (!handle)
|
||||
goto failure;
|
||||
AFileDescriptor_getFd = dlsym (handle, "AFileDescriptor_getFd");
|
||||
if (!AFileDescriptor_getFd)
|
||||
goto failure;
|
||||
}
|
||||
|
||||
method = asset_fd_class.get_file_descriptor;
|
||||
java_fd = (*android_java_env)->CallObjectMethod (android_java_env,
|
||||
afd, method);
|
||||
android_exception_check ();
|
||||
*fd = (*AFileDescriptor_getFd) (android_java_env, java_fd);
|
||||
ANDROID_DELETE_LOCAL_REF (java_fd);
|
||||
|
||||
if (*fd >= 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
failure:
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data,
|
||||
Sandroid_get_clipboard_data, 1, 1, 0,
|
||||
doc: /* Return the clipboard data of the given MIME TYPE.
|
||||
|
@ -361,12 +537,12 @@ does not have any corresponding data. In that case, use
|
|||
`android-get-clipboard' instead. */)
|
||||
(Lisp_Object type)
|
||||
{
|
||||
jlongArray array;
|
||||
jobject afd;
|
||||
jbyteArray bytes;
|
||||
jmethodID method;
|
||||
int fd;
|
||||
ptrdiff_t rc;
|
||||
jlong offset, length, *longs;
|
||||
jlong offset, length;
|
||||
specpdl_ref ref;
|
||||
char *buffer, *start;
|
||||
|
||||
|
@ -387,36 +563,25 @@ does not have any corresponding data. In that case, use
|
|||
android_exception_check ();
|
||||
|
||||
method = clipboard_class.get_clipboard_data;
|
||||
array = (*android_java_env)->CallObjectMethod (android_java_env,
|
||||
clipboard, method,
|
||||
bytes);
|
||||
afd = (*android_java_env)->CallObjectMethod (android_java_env,
|
||||
clipboard, method,
|
||||
bytes);
|
||||
android_exception_check_1 (bytes);
|
||||
ANDROID_DELETE_LOCAL_REF (bytes);
|
||||
|
||||
if (!array)
|
||||
if (!afd)
|
||||
goto fail;
|
||||
|
||||
longs = (*android_java_env)->GetLongArrayElements (android_java_env,
|
||||
array, NULL);
|
||||
android_exception_check_nonnull (longs, array);
|
||||
/* Extract the file descriptor from the AssetFileDescriptor
|
||||
object. */
|
||||
ref = SPECPDL_INDEX ();
|
||||
record_unwind_protect_ptr (close_asset_fd, &afd);
|
||||
|
||||
/* longs[0] is the file descriptor.
|
||||
longs[1] is an offset to apply to the file.
|
||||
longs[2] is either -1, or the number of bytes to read from the
|
||||
file. */
|
||||
fd = longs[0];
|
||||
offset = longs[1];
|
||||
length = longs[2];
|
||||
|
||||
(*android_java_env)->ReleaseLongArrayElements (android_java_env,
|
||||
array, longs,
|
||||
JNI_ABORT);
|
||||
ANDROID_DELETE_LOCAL_REF (array);
|
||||
if (extract_fd_offsets (afd, &fd, &offset, &length))
|
||||
return unbind_to (ref, Qnil);
|
||||
unblock_input ();
|
||||
|
||||
/* Now begin reading from longs[0]. */
|
||||
ref = SPECPDL_INDEX ();
|
||||
record_unwind_protect_int (close_file_unwind, fd);
|
||||
/* Now begin reading from fd. */
|
||||
|
||||
if (length != -1)
|
||||
{
|
||||
|
@ -1004,6 +1169,7 @@ init_androidselect (void)
|
|||
return;
|
||||
|
||||
android_init_emacs_clipboard ();
|
||||
android_init_asset_file_descriptor ();
|
||||
android_init_emacs_desktop_notification ();
|
||||
|
||||
make_clipboard = clipboard_class.make_clipboard;
|
||||
|
|
|
@ -290,17 +290,6 @@ struct emacs_directory_entry_class
|
|||
jfieldID d_name;
|
||||
};
|
||||
|
||||
/* Structure describing the android.os.ParcelFileDescriptor class used
|
||||
to wrap file descriptors sent over IPC. */
|
||||
|
||||
struct android_parcel_file_descriptor_class
|
||||
{
|
||||
jclass class;
|
||||
jmethodID close;
|
||||
jmethodID get_fd;
|
||||
jmethodID detach_fd;
|
||||
};
|
||||
|
||||
/* The java.lang.String class. */
|
||||
jclass java_string_class;
|
||||
|
||||
|
@ -313,7 +302,7 @@ static struct emacs_directory_entry_class entry_class;
|
|||
|
||||
/* Fields and methods associated with the ParcelFileDescriptor
|
||||
class. */
|
||||
static struct android_parcel_file_descriptor_class fd_class;
|
||||
struct android_parcel_file_descriptor_class fd_class;
|
||||
|
||||
/* Global references to several exception classes. */
|
||||
static jclass file_not_found_exception, security_exception;
|
||||
|
@ -380,13 +369,18 @@ android_init_entry_class (JNIEnv *env)
|
|||
}
|
||||
|
||||
|
||||
/* Initialize `fd_class' using the given JNI environment ENV. Calling
|
||||
this function is not necessary on Android 4.4 and earlier. */
|
||||
/* Initialize `fd_class' using the given JNI environment ENV. Called on
|
||||
API 12 (Android 3.1) and later by androidselect.c and on 5.0 and
|
||||
later in this file. */
|
||||
|
||||
static void
|
||||
void
|
||||
android_init_fd_class (JNIEnv *env)
|
||||
{
|
||||
jclass old;
|
||||
static bool fd_class_initialized;
|
||||
|
||||
if (fd_class_initialized)
|
||||
return;
|
||||
|
||||
fd_class.class
|
||||
= (*env)->FindClass (env, "android/os/ParcelFileDescriptor");
|
||||
|
@ -409,6 +403,8 @@ android_init_fd_class (JNIEnv *env)
|
|||
FIND_METHOD (get_fd, "getFd", "()I");
|
||||
FIND_METHOD (detach_fd, "detachFd", "()I");
|
||||
#undef FIND_METHOD
|
||||
|
||||
fd_class_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue