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/androidselect.c | |
| 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/androidselect.c')
| -rw-r--r-- | src/androidselect.c | 216 |
1 files changed, 191 insertions, 25 deletions
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; |