diff options
| author | Po Lu | 2024-05-01 11:45:53 +0800 |
|---|---|---|
| committer | Po Lu | 2024-05-01 11:46:31 +0800 |
| commit | 2451456695d0e03b89365cbbe64effb2f99af2d5 (patch) | |
| tree | b6bcd3e227454b436ab4ee0fad447d946b5179cf /src | |
| parent | 294335b2304028cc97aca036bd37adf2f8e1c508 (diff) | |
| download | emacs-2451456695d0e03b89365cbbe64effb2f99af2d5.tar.gz emacs-2451456695d0e03b89365cbbe64effb2f99af2d5.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/android.h | 16 | ||||
| -rw-r--r-- | src/androidselect.c | 216 | ||||
| -rw-r--r-- | src/androidvfs.c | 26 |
3 files changed, 218 insertions, 40 deletions
diff --git a/src/android.h b/src/android.h index 19adfa38087..7074ca2630c 100644 --- a/src/android.h +++ b/src/android.h | |||
| @@ -53,6 +53,22 @@ extern char *android_user_full_name (struct passwd *); | |||
| 53 | 53 | ||
| 54 | 54 | ||
| 55 | 55 | ||
| 56 | /* Structure describing the android.os.ParcelFileDescriptor class used | ||
| 57 | to wrap file descriptors sent over IPC. */ | ||
| 58 | |||
| 59 | struct android_parcel_file_descriptor_class | ||
| 60 | { | ||
| 61 | jclass class; | ||
| 62 | jmethodID close; | ||
| 63 | jmethodID get_fd; | ||
| 64 | jmethodID detach_fd; | ||
| 65 | }; | ||
| 66 | |||
| 67 | /* The ParcelFileDescriptor class. */ | ||
| 68 | extern struct android_parcel_file_descriptor_class fd_class; | ||
| 69 | |||
| 70 | extern void android_init_fd_class (JNIEnv *); | ||
| 71 | |||
| 56 | /* File I/O operations. Many of these are defined in | 72 | /* File I/O operations. Many of these are defined in |
| 57 | androidvfs.c. */ | 73 | androidvfs.c. */ |
| 58 | 74 | ||
diff --git a/src/androidselect.c b/src/androidselect.c index 2f6114d0fcb..04d04d326d9 100644 --- a/src/androidselect.c +++ b/src/androidselect.c | |||
| @@ -21,6 +21,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 21 | #include <assert.h> | 21 | #include <assert.h> |
| 22 | #include <minmax.h> | 22 | #include <minmax.h> |
| 23 | #include <unistd.h> | 23 | #include <unistd.h> |
| 24 | #include <dlfcn.h> | ||
| 24 | 25 | ||
| 25 | #include <boot-time.h> | 26 | #include <boot-time.h> |
| 26 | #include <sys/types.h> | 27 | #include <sys/types.h> |
| @@ -100,7 +101,7 @@ android_init_emacs_clipboard (void) | |||
| 100 | FIND_METHOD (get_clipboard_targets, "getClipboardTargets", | 101 | FIND_METHOD (get_clipboard_targets, "getClipboardTargets", |
| 101 | "()[[B"); | 102 | "()[[B"); |
| 102 | FIND_METHOD (get_clipboard_data, "getClipboardData", | 103 | FIND_METHOD (get_clipboard_data, "getClipboardData", |
| 103 | "([B)[J"); | 104 | "([B)Landroid/content/res/AssetFileDescriptor;"); |
| 104 | 105 | ||
| 105 | clipboard_class.make_clipboard | 106 | clipboard_class.make_clipboard |
| 106 | = (*android_java_env)->GetStaticMethodID (android_java_env, | 107 | = (*android_java_env)->GetStaticMethodID (android_java_env, |
| @@ -340,6 +341,62 @@ data type available from the clipboard. */) | |||
| 340 | return Qnil; | 341 | return Qnil; |
| 341 | } | 342 | } |
| 342 | 343 | ||
| 344 | |||
| 345 | |||
| 346 | struct android_asset_file_descriptor | ||
| 347 | { | ||
| 348 | jclass class; | ||
| 349 | jmethodID close; | ||
| 350 | jmethodID get_length; | ||
| 351 | jmethodID get_start_offset; | ||
| 352 | jmethodID get_file_descriptor; | ||
| 353 | jmethodID get_parcel_file_descriptor; | ||
| 354 | jmethodID get_fd; | ||
| 355 | }; | ||
| 356 | |||
| 357 | /* Methods associated with the AssetFileDescriptor class. */ | ||
| 358 | static struct android_asset_file_descriptor asset_fd_class; | ||
| 359 | |||
| 360 | /* Initialize virtual function IDs and class pointers in connection with | ||
| 361 | the AssetFileDescriptor class. */ | ||
| 362 | |||
| 363 | static void | ||
| 364 | android_init_asset_file_descriptor (void) | ||
| 365 | { | ||
| 366 | jclass old; | ||
| 367 | |||
| 368 | asset_fd_class.class | ||
| 369 | = (*android_java_env)->FindClass (android_java_env, | ||
| 370 | "android/content/res/" | ||
| 371 | "AssetFileDescriptor"); | ||
| 372 | eassert (asset_fd_class.class); | ||
| 373 | |||
| 374 | old = asset_fd_class.class; | ||
| 375 | asset_fd_class.class | ||
| 376 | = (jclass) (*android_java_env)->NewGlobalRef (android_java_env, | ||
| 377 | old); | ||
| 378 | ANDROID_DELETE_LOCAL_REF (old); | ||
| 379 | |||
| 380 | if (!asset_fd_class.class) | ||
| 381 | emacs_abort (); | ||
| 382 | |||
| 383 | #define FIND_METHOD(c_name, name, signature) \ | ||
| 384 | asset_fd_class.c_name \ | ||
| 385 | = (*android_java_env)->GetMethodID (android_java_env, \ | ||
| 386 | asset_fd_class.class, \ | ||
| 387 | name, signature); \ | ||
| 388 | eassert (asset_fd_class.c_name); | ||
| 389 | |||
| 390 | FIND_METHOD (close, "close", "()V"); | ||
| 391 | FIND_METHOD (get_length, "getLength", "()J"); | ||
| 392 | FIND_METHOD (get_start_offset, "getStartOffset", "()J"); | ||
| 393 | FIND_METHOD (get_file_descriptor, "getFileDescriptor", | ||
| 394 | "()Ljava/io/FileDescriptor;"); | ||
| 395 | FIND_METHOD (get_parcel_file_descriptor, "getParcelFileDescriptor", | ||
| 396 | "()Landroid/os/ParcelFileDescriptor;"); | ||
| 397 | #undef FIND_METHOD | ||
| 398 | } | ||
| 399 | |||
| 343 | /* Free the memory inside PTR, a pointer to a char pointer. */ | 400 | /* Free the memory inside PTR, a pointer to a char pointer. */ |
| 344 | 401 | ||
| 345 | static void | 402 | static void |
| @@ -348,6 +405,125 @@ android_xfree_inside (void *ptr) | |||
| 348 | xfree (*(char **) ptr); | 405 | xfree (*(char **) ptr); |
| 349 | } | 406 | } |
| 350 | 407 | ||
| 408 | /* Close the referent of, then delete, the local reference to an asset | ||
| 409 | file descriptor referenced by AFD. */ | ||
| 410 | |||
| 411 | static void | ||
| 412 | close_asset_fd (void *afd) | ||
| 413 | { | ||
| 414 | jobject *afd_1; | ||
| 415 | |||
| 416 | afd_1 = afd; | ||
| 417 | (*android_java_env)->CallVoidMethod (android_java_env, *afd_1, | ||
| 418 | asset_fd_class.close); | ||
| 419 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 420 | ANDROID_DELETE_LOCAL_REF (*afd_1); | ||
| 421 | } | ||
| 422 | |||
| 423 | /* Return the offset, file descriptor and length of the data contained | ||
| 424 | in the asset file descriptor AFD, in *FD, *OFFSET, and *LENGTH. | ||
| 425 | Value is 0 upon success, 1 otherwise. */ | ||
| 426 | |||
| 427 | static int | ||
| 428 | extract_fd_offsets (jobject afd, int *fd, jlong *offset, jlong *length) | ||
| 429 | { | ||
| 430 | jobject java_fd; | ||
| 431 | void *handle; | ||
| 432 | #if __ANDROID_API__ <= 11 | ||
| 433 | static int (*jniGetFDFromFileDescriptor) (JNIEnv *, jobject); | ||
| 434 | #endif /* __ANDROID_API__ <= 11 */ | ||
| 435 | static int (*AFileDescriptor_getFd) (JNIEnv *, jobject);; | ||
| 436 | jmethodID method; | ||
| 437 | |||
| 438 | method = asset_fd_class.get_start_offset; | ||
| 439 | *offset = (*android_java_env)->CallLongMethod (android_java_env, | ||
| 440 | afd, method); | ||
| 441 | android_exception_check (); | ||
| 442 | method = asset_fd_class.get_length; | ||
| 443 | *length = (*android_java_env)->CallLongMethod (android_java_env, | ||
| 444 | afd, method); | ||
| 445 | android_exception_check (); | ||
| 446 | |||
| 447 | #if __ANDROID_API__ <= 11 | ||
| 448 | if (android_get_current_api_level () <= 11) | ||
| 449 | { | ||
| 450 | /* Load libnativehelper and link to a private interface that is | ||
| 451 | the only means of retrieving the file descriptor from an asset | ||
| 452 | file descriptor on these systems. */ | ||
| 453 | |||
| 454 | if (!jniGetFDFromFileDescriptor) | ||
| 455 | { | ||
| 456 | handle = dlopen ("libnativehelper.so", | ||
| 457 | RTLD_LAZY | RTLD_GLOBAL); | ||
| 458 | if (!handle) | ||
| 459 | goto failure; | ||
| 460 | jniGetFdFromFileDescriptor = dlsym (handle, | ||
| 461 | "jniGetFDFromFileDescriptor"); | ||
| 462 | if (!jniGetFdFromFileDescriptor) | ||
| 463 | goto failure; | ||
| 464 | } | ||
| 465 | |||
| 466 | method = asset_fd_class.get_file_descriptor; | ||
| 467 | java_fd = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 468 | afd, method); | ||
| 469 | android_exception_check (); | ||
| 470 | *fd = (*jniGetFDFromFileDescriptor) (android_java_env, java_fd); | ||
| 471 | ANDROID_DELETE_LOCAL_REF (java_fd); | ||
| 472 | |||
| 473 | if (*fd >= 0) | ||
| 474 | return 0; | ||
| 475 | } | ||
| 476 | else | ||
| 477 | #endif /* __ANDROID_API__ <= 11 */ | ||
| 478 | #if __ANDROID_API__ <= 30 | ||
| 479 | if (android_get_current_api_level () <= 30) | ||
| 480 | { | ||
| 481 | /* Convert this AssetFileDescriptor into a ParcelFileDescriptor, | ||
| 482 | whose getFd method will return its native file descriptor. */ | ||
| 483 | method = asset_fd_class.get_parcel_file_descriptor; | ||
| 484 | java_fd = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 485 | afd, method); | ||
| 486 | android_exception_check (); | ||
| 487 | |||
| 488 | /* Initialize fd_class if not already complete. */ | ||
| 489 | android_init_fd_class (android_java_env); | ||
| 490 | *fd = (*android_java_env)->CallIntMethod (android_java_env, | ||
| 491 | java_fd, | ||
| 492 | fd_class.get_fd); | ||
| 493 | if (*fd >= 0) | ||
| 494 | return 0; | ||
| 495 | } | ||
| 496 | else | ||
| 497 | #endif /* __ANDROID_API__ <= 30 */ | ||
| 498 | { | ||
| 499 | /* Load libnativehelper (now a public interface) and link to | ||
| 500 | AFileDescriptor_getFd. */ | ||
| 501 | if (!AFileDescriptor_getFd) | ||
| 502 | { | ||
| 503 | handle = dlopen ("libnativehelper.so", | ||
| 504 | RTLD_LAZY | RTLD_GLOBAL); | ||
| 505 | if (!handle) | ||
| 506 | goto failure; | ||
| 507 | AFileDescriptor_getFd = dlsym (handle, "AFileDescriptor_getFd"); | ||
| 508 | if (!AFileDescriptor_getFd) | ||
| 509 | goto failure; | ||
| 510 | } | ||
| 511 | |||
| 512 | method = asset_fd_class.get_file_descriptor; | ||
| 513 | java_fd = (*android_java_env)->CallObjectMethod (android_java_env, | ||
| 514 | afd, method); | ||
| 515 | android_exception_check (); | ||
| 516 | *fd = (*AFileDescriptor_getFd) (android_java_env, java_fd); | ||
| 517 | ANDROID_DELETE_LOCAL_REF (java_fd); | ||
| 518 | |||
| 519 | if (*fd >= 0) | ||
| 520 | return 0; | ||
| 521 | } | ||
| 522 | |||
| 523 | failure: | ||
| 524 | return 1; | ||
| 525 | } | ||
| 526 | |||
| 351 | DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data, | 527 | DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data, |
| 352 | Sandroid_get_clipboard_data, 1, 1, 0, | 528 | Sandroid_get_clipboard_data, 1, 1, 0, |
| 353 | doc: /* Return the clipboard data of the given MIME TYPE. | 529 | doc: /* Return the clipboard data of the given MIME TYPE. |
| @@ -361,12 +537,12 @@ does not have any corresponding data. In that case, use | |||
| 361 | `android-get-clipboard' instead. */) | 537 | `android-get-clipboard' instead. */) |
| 362 | (Lisp_Object type) | 538 | (Lisp_Object type) |
| 363 | { | 539 | { |
| 364 | jlongArray array; | 540 | jobject afd; |
| 365 | jbyteArray bytes; | 541 | jbyteArray bytes; |
| 366 | jmethodID method; | 542 | jmethodID method; |
| 367 | int fd; | 543 | int fd; |
| 368 | ptrdiff_t rc; | 544 | ptrdiff_t rc; |
| 369 | jlong offset, length, *longs; | 545 | jlong offset, length; |
| 370 | specpdl_ref ref; | 546 | specpdl_ref ref; |
| 371 | char *buffer, *start; | 547 | char *buffer, *start; |
| 372 | 548 | ||
| @@ -387,36 +563,25 @@ does not have any corresponding data. In that case, use | |||
| 387 | android_exception_check (); | 563 | android_exception_check (); |
| 388 | 564 | ||
| 389 | method = clipboard_class.get_clipboard_data; | 565 | method = clipboard_class.get_clipboard_data; |
| 390 | array = (*android_java_env)->CallObjectMethod (android_java_env, | 566 | afd = (*android_java_env)->CallObjectMethod (android_java_env, |
| 391 | clipboard, method, | 567 | clipboard, method, |
| 392 | bytes); | 568 | bytes); |
| 393 | android_exception_check_1 (bytes); | 569 | android_exception_check_1 (bytes); |
| 394 | ANDROID_DELETE_LOCAL_REF (bytes); | 570 | ANDROID_DELETE_LOCAL_REF (bytes); |
| 395 | 571 | ||
| 396 | if (!array) | 572 | if (!afd) |
| 397 | goto fail; | 573 | goto fail; |
| 398 | 574 | ||
| 399 | longs = (*android_java_env)->GetLongArrayElements (android_java_env, | 575 | /* Extract the file descriptor from the AssetFileDescriptor |
| 400 | array, NULL); | 576 | object. */ |
| 401 | android_exception_check_nonnull (longs, array); | 577 | ref = SPECPDL_INDEX (); |
| 402 | 578 | record_unwind_protect_ptr (close_asset_fd, &afd); | |
| 403 | /* longs[0] is the file descriptor. | ||
| 404 | longs[1] is an offset to apply to the file. | ||
| 405 | longs[2] is either -1, or the number of bytes to read from the | ||
| 406 | file. */ | ||
| 407 | fd = longs[0]; | ||
| 408 | offset = longs[1]; | ||
| 409 | length = longs[2]; | ||
| 410 | 579 | ||
| 411 | (*android_java_env)->ReleaseLongArrayElements (android_java_env, | 580 | if (extract_fd_offsets (afd, &fd, &offset, &length)) |
| 412 | array, longs, | 581 | return unbind_to (ref, Qnil); |
| 413 | JNI_ABORT); | ||
| 414 | ANDROID_DELETE_LOCAL_REF (array); | ||
| 415 | unblock_input (); | 582 | unblock_input (); |
| 416 | 583 | ||
| 417 | /* Now begin reading from longs[0]. */ | 584 | /* Now begin reading from fd. */ |
| 418 | ref = SPECPDL_INDEX (); | ||
| 419 | record_unwind_protect_int (close_file_unwind, fd); | ||
| 420 | 585 | ||
| 421 | if (length != -1) | 586 | if (length != -1) |
| 422 | { | 587 | { |
| @@ -1004,6 +1169,7 @@ init_androidselect (void) | |||
| 1004 | return; | 1169 | return; |
| 1005 | 1170 | ||
| 1006 | android_init_emacs_clipboard (); | 1171 | android_init_emacs_clipboard (); |
| 1172 | android_init_asset_file_descriptor (); | ||
| 1007 | android_init_emacs_desktop_notification (); | 1173 | android_init_emacs_desktop_notification (); |
| 1008 | 1174 | ||
| 1009 | make_clipboard = clipboard_class.make_clipboard; | 1175 | make_clipboard = clipboard_class.make_clipboard; |
diff --git a/src/androidvfs.c b/src/androidvfs.c index c4b3dba4af0..38bec7d349a 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c | |||
| @@ -290,17 +290,6 @@ struct emacs_directory_entry_class | |||
| 290 | jfieldID d_name; | 290 | jfieldID d_name; |
| 291 | }; | 291 | }; |
| 292 | 292 | ||
| 293 | /* Structure describing the android.os.ParcelFileDescriptor class used | ||
| 294 | to wrap file descriptors sent over IPC. */ | ||
| 295 | |||
| 296 | struct android_parcel_file_descriptor_class | ||
| 297 | { | ||
| 298 | jclass class; | ||
| 299 | jmethodID close; | ||
| 300 | jmethodID get_fd; | ||
| 301 | jmethodID detach_fd; | ||
| 302 | }; | ||
| 303 | |||
| 304 | /* The java.lang.String class. */ | 293 | /* The java.lang.String class. */ |
| 305 | jclass java_string_class; | 294 | jclass java_string_class; |
| 306 | 295 | ||
| @@ -313,7 +302,7 @@ static struct emacs_directory_entry_class entry_class; | |||
| 313 | 302 | ||
| 314 | /* Fields and methods associated with the ParcelFileDescriptor | 303 | /* Fields and methods associated with the ParcelFileDescriptor |
| 315 | class. */ | 304 | class. */ |
| 316 | static struct android_parcel_file_descriptor_class fd_class; | 305 | struct android_parcel_file_descriptor_class fd_class; |
| 317 | 306 | ||
| 318 | /* Global references to several exception classes. */ | 307 | /* Global references to several exception classes. */ |
| 319 | static jclass file_not_found_exception, security_exception; | 308 | static jclass file_not_found_exception, security_exception; |
| @@ -380,13 +369,18 @@ android_init_entry_class (JNIEnv *env) | |||
| 380 | } | 369 | } |
| 381 | 370 | ||
| 382 | 371 | ||
| 383 | /* Initialize `fd_class' using the given JNI environment ENV. Calling | 372 | /* Initialize `fd_class' using the given JNI environment ENV. Called on |
| 384 | this function is not necessary on Android 4.4 and earlier. */ | 373 | API 12 (Android 3.1) and later by androidselect.c and on 5.0 and |
| 374 | later in this file. */ | ||
| 385 | 375 | ||
| 386 | static void | 376 | void |
| 387 | android_init_fd_class (JNIEnv *env) | 377 | android_init_fd_class (JNIEnv *env) |
| 388 | { | 378 | { |
| 389 | jclass old; | 379 | jclass old; |
| 380 | static bool fd_class_initialized; | ||
| 381 | |||
| 382 | if (fd_class_initialized) | ||
| 383 | return; | ||
| 390 | 384 | ||
| 391 | fd_class.class | 385 | fd_class.class |
| 392 | = (*env)->FindClass (env, "android/os/ParcelFileDescriptor"); | 386 | = (*env)->FindClass (env, "android/os/ParcelFileDescriptor"); |
| @@ -409,6 +403,8 @@ android_init_fd_class (JNIEnv *env) | |||
| 409 | FIND_METHOD (get_fd, "getFd", "()I"); | 403 | FIND_METHOD (get_fd, "getFd", "()I"); |
| 410 | FIND_METHOD (detach_fd, "detachFd", "()I"); | 404 | FIND_METHOD (detach_fd, "detachFd", "()I"); |
| 411 | #undef FIND_METHOD | 405 | #undef FIND_METHOD |
| 406 | |||
| 407 | fd_class_initialized = true; | ||
| 412 | } | 408 | } |
| 413 | 409 | ||
| 414 | 410 | ||